diff --git a/tiberiandawn/conquer.cpp b/tiberiandawn/conquer.cpp index a8aebd8e..93c3fe94 100644 --- a/tiberiandawn/conquer.cpp +++ b/tiberiandawn/conquer.cpp @@ -3947,6 +3947,41 @@ void Blit_Hid_Page_To_Seen_Buff(void) HidPage.Blit(SeenBuff); } +/*********************************************************************************************** + * 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, "nod") == 0) { + ownable |= HOUSEF_BAD; + } else { + if (stricmp(text, "gdi") == 0) { + ownable |= HOUSEF_GOOD; + } else { + HousesType h = HouseTypeClass::From_Name(text); + if (h != HOUSE_NONE && (h < HOUSE_MULTI1 || h > HOUSE_MULTI6)) { + ownable |= (1 << h); + } + } + } + return (ownable); +} + /*********************************************************************************************** * Shake_The_Screen -- Dispatcher that shakes the screen. * * * diff --git a/tiberiandawn/defines.h b/tiberiandawn/defines.h index 79269148..e7401bdf 100644 --- a/tiberiandawn/defines.h +++ b/tiberiandawn/defines.h @@ -685,6 +685,9 @@ typedef enum HousesType : signed char #define HOUSEF_MULTI4 (1 << HOUSE_MULTI4) #define HOUSEF_MULTI5 (1 << HOUSE_MULTI5) #define HOUSEF_MULTI6 (1 << HOUSE_MULTI6) +#define HOUSEF_OTHERS \ + (HOUSEF_NEUTRAL | HOUSEF_JP | HOUSEF_MULTI1 | HOUSEF_MULTI2 | HOUSEF_MULTI3 | HOUSEF_MULTI4 | HOUSEF_MULTI5 \ + | HOUSEF_MULTI6) typedef enum PlayerColorType : signed char { diff --git a/tiberiandawn/function.h b/tiberiandawn/function.h index d5df0698..905e4e16 100644 --- a/tiberiandawn/function.h +++ b/tiberiandawn/function.h @@ -301,6 +301,7 @@ void Explosion_Damage(COORDINATE coord, unsigned strength, TechnoClass* source, /* ** CONQUER.CPP */ +int Owner_From_Name(char const* text); void Center_About_Objects(void); bool Force_CD_Available(int cd); void Handle_View(int view, int action = 0); diff --git a/tiberiandawn/rules.cpp b/tiberiandawn/rules.cpp index 9826123d..fde81546 100644 --- a/tiberiandawn/rules.cpp +++ b/tiberiandawn/rules.cpp @@ -504,6 +504,82 @@ bool RulesClass::Export_AI(CCINIClass& ini) ini.Put_Fixed(AI, "PowerEmergency", PowerEmergencyFraction); 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_GOOD | HOUSEF_BAD | 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); +} + +bool RulesClass::Export_Themes(CCINIClass& ini) +{ + static char const* const THEMECONTROL = "ThemeControl"; + + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + + char buffer[128]; + int scen = Theme.Scenario(theme); + int owners = Theme.Owner(theme); + + const char* owner_name; + switch (owners) { + case HOUSEF_GOOD: + owner_name = ",GDI"; + break; + case HOUSEF_BAD: + owner_name = ",Nod"; + break; + default: + owner_name = ""; + break; + } + + snprintf(buffer, sizeof(buffer), "%d,%s", scen, owner_name); + ini.Put_String(THEMECONTROL, Theme.Base_Name(theme), buffer); + } + + return true; +} /*********************************************************************************************** * RulesClass::IQ -- Fetches the IQ control values from the INI database. * diff --git a/tiberiandawn/rules.h b/tiberiandawn/rules.h index 0ee8ddd6..3bec3cd5 100644 --- a/tiberiandawn/rules.h +++ b/tiberiandawn/rules.h @@ -69,12 +69,14 @@ class RulesClass bool General(CCINIClass& ini); bool Recharge(CCINIClass& ini); bool AI(CCINIClass& ini); + bool Themes(CCINIClass& ini); bool IQ(CCINIClass& ini); bool Difficulty(CCINIClass& ini); bool Export(CCINIClass& file); bool Export_General(CCINIClass& ini); bool Export_Recharge(CCINIClass& ini); bool Export_AI(CCINIClass& ini); + bool Export_Themes(CCINIClass& ini); bool Export_IQ(CCINIClass& ini); bool Export_Difficulty(CCINIClass& ini); diff --git a/tiberiandawn/theme.cpp b/tiberiandawn/theme.cpp index 972a6dc9..73023462 100644 --- a/tiberiandawn/theme.cpp +++ b/tiberiandawn/theme.cpp @@ -54,44 +54,44 @@ ** These are the actual filename list for the theme sample files. */ ThemeClass::ThemeControl ThemeClass::_themes[THEME_COUNT] = { - {"AIRSTRIK", TXT_THEME_AIRSTRIKE, 0, 200, false, false, false, true}, - {"80MX226M", TXT_THEME_80MX, 0, 248, false, false, false, true}, - {"CHRG226M", TXT_THEME_CHRG, 0, 256, true, false, false, true}, - {"CREP226M", TXT_THEME_CREP, 0, 222, true, false, false, true}, - {"DRIL226M", TXT_THEME_DRIL, 0, 272, true, false, false, true}, - {"DRON226M", TXT_THEME_DRON, 0, 275, true, false, false, true}, - {"FIST226M", TXT_THEME_FIST, 0, 212, true, false, false, true}, - {"RECN226M", TXT_THEME_RECON, 0, 261, true, false, false, true}, - {"VOIC226M", TXT_THEME_VOICE, 0, 306, true, false, false, true}, - {"HEAVYG", TXT_THEME_HEAVYG, 0, 180, true, false, false, true}, - {"J1", TXT_THEME_J1, 4, 187, true, false, false, true}, + {"AIRSTRIK", TXT_THEME_AIRSTRIKE, 0, 200, false, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"80MX226M", TXT_THEME_80MX, 0, 248, false, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"CHRG226M", TXT_THEME_CHRG, 0, 256, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"CREP226M", TXT_THEME_CREP, 0, 222, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"DRIL226M", TXT_THEME_DRIL, 0, 272, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"DRON226M", TXT_THEME_DRON, 0, 275, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"FIST226M", TXT_THEME_FIST, 0, 212, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"RECN226M", TXT_THEME_RECON, 0, 261, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"VOIC226M", TXT_THEME_VOICE, 0, 306, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"HEAVYG", TXT_THEME_HEAVYG, 0, 180, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"J1", TXT_THEME_J1, 4, 187, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, // {"J1", TXT_THEME_J1, 4, 187, false, false,false,true}, - {"JDI_V2", TXT_THEME_JDI_V2, 5, 183, true, false, false, true}, - {"RADIO", TXT_THEME_RADIO, 6, 183, true, false, false, true}, - {"RAIN", TXT_THEME_RAIN, 7, 156, true, false, false, true}, - {"AOI", TXT_THEME_AOI, 0, 168, true, true, false, true}, - {"CCTHANG", TXT_THEME_CCTHANG, 12, 193, true, false, false, true}, - {"DIE", TXT_THEME_DIE, 11, 162, false, false, false, true}, - {"FWP", TXT_THEME_FWP, 10, 53, true, false, false, true}, - {"IND", TXT_THEME_IND, 1, 175, true, false, false, true}, - {"IND2", TXT_THEME_IND2, 1, 38, true, false, false, true}, - {"JUSTDOIT", TXT_THEME_JUSTDOIT, 9, 142, true, false, false, true}, - {"LINEFIRE", TXT_THEME_LINEFIRE, 8, 125, true, false, false, true}, - {"MARCH", TXT_THEME_MARCH, 7, 157, true, false, false, true}, - {"TARGET", TXT_THEME_TARGET, 0, 173, true, false, false, true}, - {"NOMERCY", TXT_THEME_NOMERCY, 2, 204, true, false, false, true}, - {"OTP", TXT_THEME_OTP, 3, 182, true, false, false, true}, - {"PRP", TXT_THEME_PRP, 4, 211, true, false, false, true}, - {"ROUT", TXT_THEME_ROUT, 12, 121, false, true, false, true}, - {"HEART", TXT_THEME_HEART, 5, 206, false, true, false, true}, - {"STOPTHEM", TXT_THEME_STOPTHEM, 0, 190, true, false, false, true}, - {"TROUBLE", TXT_THEME_TROUBLE, 6, 191, true, true, false, true}, - {"WARFARE", TXT_THEME_WARFARE, 0, 182, true, false, false, true}, - {"BEFEARED", TXT_THEME_BEFEARED, 13, 164, false, true, false, true}, - {"I_AM", TXT_THEME_IAM, 6, 161, false, false, false, true}, - {"WIN1", TXT_THEME_WIN1, 0, 41, false, true, true, true}, - {"MAP1", TXT_THEME_WIN1, 0, 61, false, false, true, true}, - {"VALKYRIE", TXT_THEME_VALK, 0, 306, false, false, true, true}, + {"JDI_V2", TXT_THEME_JDI_V2, 5, 183, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"RADIO", TXT_THEME_RADIO, 6, 183, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"RAIN", TXT_THEME_RAIN, 7, 156, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"AOI", TXT_THEME_AOI, 0, 168, true, true, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"CCTHANG", TXT_THEME_CCTHANG, 12, 193, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"DIE", TXT_THEME_DIE, 11, 162, false, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"FWP", TXT_THEME_FWP, 10, 53, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"IND", TXT_THEME_IND, 1, 175, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"IND2", TXT_THEME_IND2, 1, 38, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"JUSTDOIT", TXT_THEME_JUSTDOIT, 9, 142, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"LINEFIRE", TXT_THEME_LINEFIRE, 8, 125, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"MARCH", TXT_THEME_MARCH, 7, 157, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"TARGET", TXT_THEME_TARGET, 0, 173, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"NOMERCY", TXT_THEME_NOMERCY, 2, 204, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"OTP", TXT_THEME_OTP, 3, 182, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"PRP", TXT_THEME_PRP, 4, 211, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"ROUT", TXT_THEME_ROUT, 12, 121, false, true, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"HEART", TXT_THEME_HEART, 5, 206, false, true, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"STOPTHEM", TXT_THEME_STOPTHEM, 0, 190, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"TROUBLE", TXT_THEME_TROUBLE, 6, 191, true, true, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"WARFARE", TXT_THEME_WARFARE, 0, 182, true, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"BEFEARED", TXT_THEME_BEFEARED, 13, 164, false, true, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"I_AM", TXT_THEME_IAM, 6, 161, false, false, false, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"WIN1", TXT_THEME_WIN1, 0, 41, false, true, true, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"MAP1", TXT_THEME_WIN1, 0, 61, false, false, true, true, HOUSEF_GOOD | HOUSEF_BAD}, + {"VALKYRIE", TXT_THEME_VALK, 0, 306, false, false, true, true, HOUSEF_GOOD | HOUSEF_BAD}, }; /*********************************************************************************************** @@ -467,12 +467,43 @@ bool ThemeClass::Is_Allowed(ThemeType index) const } } - return (_themes[index].Available - && (_themes[index].Normal || - // (index == THEME_MAP1 && ScenarioInit) || - ((Special.IsVariation && _themes[index].Variation && index != THEME_WIN1) - && (!Is_Demo() || (GameToPlay != GAME_NORMAL || _themes[index].Scenario <= (int)Scen.Scenario)) - && (index != THEME_J1 || Special.IsJurassic)))); + 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 (!Special.IsVariation || !_themes[index].Variation || index == THEME_WIN1) + 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 (Is_Demo() || (GameToPlay == GAME_NORMAL && Scen.Scenario < _themes[index].Scenario)) + return (false); + + /* + ** Since all otehr tests passed, return if its not the special dino theme. + */ + return (index != THEME_J1 || Special.IsJurassic); } /*********************************************************************************************** @@ -551,3 +582,32 @@ void ThemeClass::Scan(void) // } } } + +/*********************************************************************************************** + * 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/tiberiandawn/theme.h b/tiberiandawn/theme.h index 8c0c0caa..b4a527d3 100644 --- a/tiberiandawn/theme.h +++ b/tiberiandawn/theme.h @@ -54,6 +54,7 @@ class ThemeClass bool Variation; // Is there a variation to the score? 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]; @@ -89,8 +90,19 @@ class ThemeClass }; int Still_Playing(void); ThemeType Next_Song(ThemeType index); + void Set_Theme_Data(ThemeType theme, int scenario, int owners); bool Is_Allowed(ThemeType index) const; static void /*_pascal*/ Scan(void); + + static int Scenario(ThemeType index) + { + return _themes[index].Scenario; + } + + static int Owner(ThemeType index) + { + return _themes[index].Owner; + } }; #endif