diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 02e19c896..9837f6f15 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,8 +1,22 @@ -February 15, 2009 (Changes by Graf Zahl) +February 17, 2009 +- Fixed: Player names and chat macros that end with incomplete \c escapes now + have those escapes stripped before printing so that they do not merge with + subsequent text. + +February 15, 2009 (Changes by Graf Zahl) - Fixed: The CHARFORMAT structure that is used to set the color in a Windows Rich Edit control was not fully initialized resulting in incorrect colors being set. +Feburary 14, 2009 +- Moved default weapon slot assignments into the player classes. + Weapon.SlotNumber is now used solely for mods that want to add new weapons + without completely redoing the player's arsenal. Restored some config-based + weapon slot customization, though slots are no longer automatically saved + to the config and section names have changed slightly. However, unlike + before, config slots are now the definitive word on slot assignments and + cannot be overridden by any other files loaded. + February 14, 2009 (Changes by Graf Zahl) - Added MF5_CANTSEEK flag to prevent seeker missiles from homing in on certain actors and added an option to APowerInvisibility to set this @@ -22,6 +36,14 @@ February 14, 2009 (Changes by Graf Zahl) - Fixed: The MAPINFO parser ignored missing terminating braces of the last block in the file. +February 11, 2009 +- Fixed: Several weapons were missing a game filter from their definitions. +- Removed storage of weapon slots in the config so that weapon slots can + be setup in the weapons themselves. Slots are still configurable, since + they need to be for KEYCONF to work; any changes simply won't be saved + when you quit. +- Removed limit on weapon slot sizes. + February 10, 2009 - Fixed: DirectInput8Create() used the wrong calling convention. - Moved the V_InitFontColors() call earlier in the startup sequence so that diff --git a/src/cmdlib.cpp b/src/cmdlib.cpp index 717ee4aba..1ddba115f 100644 --- a/src/cmdlib.cpp +++ b/src/cmdlib.cpp @@ -10,6 +10,8 @@ #include "doomtype.h" #include "cmdlib.h" #include "i_system.h" +#include "v_text.h" + #include #include #include @@ -590,6 +592,35 @@ FString strbin1 (const char *start) return result; } +//========================================================================== +// +// CleanseString +// +// Does some mild sanity checking on a string: If it ends with an incomplete +// color escape, the escape is removed. +// +//========================================================================== + +void CleanseString(char *str) +{ + char *escape = strrchr(str, TEXTCOLOR_ESCAPE); + if (escape != NULL) + { + if (escape[1] == '\0') + { + *escape = '\0'; + } + else if (escape[1] == '[') + { + char *close = strchr(escape + 2, ']'); + if (close == NULL) + { + *escape = '\0'; + } + } + } +} + //========================================================================== // // ExpandEnvVars diff --git a/src/cmdlib.h b/src/cmdlib.h index 538329e7a..55b4a61e9 100644 --- a/src/cmdlib.h +++ b/src/cmdlib.h @@ -53,6 +53,7 @@ const char *myasctime (); int strbin (char *str); FString strbin1 (const char *start); +void CleanseString (char *str); void CreatePath(const char * fn); diff --git a/src/configfile.cpp b/src/configfile.cpp index faca98562..2a6d449a4 100644 --- a/src/configfile.cpp +++ b/src/configfile.cpp @@ -251,7 +251,8 @@ bool FConfigFile::SetSection (const char *name, bool allowCreate) // // FConfigFile :: SetFirstSection // -// Sets the current section to the first one in the file. +// Sets the current section to the first one in the file. Returns +// false if there are no sections. // //==================================================================== @@ -270,7 +271,8 @@ bool FConfigFile::SetFirstSection () // // FConfigFile :: SetNextSection // -// Advances the current section to the next one in the file. +// Advances the current section to the next one in the file. Returns +// false if there are no more sections. // //==================================================================== @@ -332,6 +334,43 @@ void FConfigFile::ClearCurrentSection () } } +//==================================================================== +// +// FConfigFile :: DeleteCurrentSection +// +// Completely removes the current section. The current section is +// advanced to the next section. Returns true if there is still a +// current section. +// +//==================================================================== + +bool FConfigFile::DeleteCurrentSection() +{ + if (CurrentSection != NULL) + { + FConfigSection *sec; + + ClearCurrentSection(); + + // Find the preceding section. + for (sec = Sections; sec != NULL && sec->Next != CurrentSection; sec = sec->Next) + { } + + sec->Next = CurrentSection->Next; + if (LastSectionPtr == &CurrentSection->Next) + { + LastSectionPtr = &sec->Next; + } + + CurrentSection->~FConfigSection(); + delete[] (char *)CurrentSection; + + CurrentSection = sec->Next; + return CurrentSection != NULL; + } + return false; +} + //==================================================================== // // FConfigFile :: ClearKey diff --git a/src/configfile.h b/src/configfile.h index ca3989415..0a058e40f 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -59,6 +59,7 @@ public: bool SetNextSection (); const char *GetCurrentSection () const; void ClearCurrentSection (); + bool DeleteCurrentSection (); void ClearKey (const char *key); bool SectionIsEmpty (); diff --git a/src/d_main.cpp b/src/d_main.cpp index 4627da8e3..34d9eb4ac 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2525,9 +2525,6 @@ void D_DoomMain (void) FActorInfo::StaticInit (); - // Now that all actors have been defined we can finally set up the weapon slots - GameConfig->DoWeaponSetup (GameNames[gameinfo.gametype]); - // [GRB] Initialize player class list SetupPlayerClasses (); diff --git a/src/d_net.cpp b/src/d_net.cpp index ed98a1a69..fd092b49e 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1963,15 +1963,16 @@ void Net_DoCommand (int type, BYTE **stream, int player) BYTE who = ReadByte (stream); s = ReadString (stream); + CleanseString (s); if (((who & 1) == 0) || players[player].userinfo.team == TEAM_NONE) { // Said to everyone if (who & 2) { - Printf (PRINT_CHAT, TEXTCOLOR_BOLD "* %s%s\n", name, s); + Printf (PRINT_CHAT, TEXTCOLOR_BOLD "* %s" TEXTCOLOR_BOLD "%s" TEXTCOLOR_BOLD "\n", name, s); } else { - Printf (PRINT_CHAT, "%s: %s\n", name, s); + Printf (PRINT_CHAT, "%s" TEXTCOLOR_CHAT ": %s" TEXTCOLOR_CHAT "\n", name, s); } S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.chatSound, 1, ATTN_NONE); } @@ -1979,11 +1980,11 @@ void Net_DoCommand (int type, BYTE **stream, int player) { // Said only to members of the player's team if (who & 2) { - Printf (PRINT_TEAMCHAT, TEXTCOLOR_BOLD "* (%s)%s\n", name, s); + Printf (PRINT_TEAMCHAT, TEXTCOLOR_BOLD "* (%s" TEXTCOLOR_BOLD ")%s" TEXTCOLOR_BOLD "\n", name, s); } else { - Printf (PRINT_TEAMCHAT, "(%s): %s\n", name, s); + Printf (PRINT_TEAMCHAT, "(%s" TEXTCOLOR_TEAMCHAT "): %s" TEXTCOLOR_TEAMCHAT "\n", name, s); } S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.chatSound, 1, ATTN_NONE); } diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index 5ed0a9ac5..3c94a4703 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -55,6 +55,7 @@ #include "teaminfo.h" #include "r_translate.h" #include "templates.h" +#include "cmdlib.h" static FRandom pr_pickteam ("PickRandomTeam"); @@ -698,10 +699,10 @@ void D_ReadUserInfoStrings (int i, BYTE **stream, bool update) { char oldname[MAXPLAYERNAME+1]; - strncpy (oldname, info->netname, MAXPLAYERNAME); - oldname[MAXPLAYERNAME] = 0; + strcpy (oldname, info->netname); strncpy (info->netname, value, MAXPLAYERNAME); info->netname[MAXPLAYERNAME] = 0; + CleanseString(info->netname); if (update && strcmp (oldname, info->netname) != 0) { diff --git a/src/d_player.h b/src/d_player.h index e43c0f18c..b78ae5a30 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -59,6 +59,16 @@ enum APMETA_Hexenarmor2, APMETA_Hexenarmor3, APMETA_Hexenarmor4, + APMETA_Slot0, + APMETA_Slot1, + APMETA_Slot2, + APMETA_Slot3, + APMETA_Slot4, + APMETA_Slot5, + APMETA_Slot6, + APMETA_Slot7, + APMETA_Slot8, + APMETA_Slot9, }; class player_t; diff --git a/src/g_game.cpp b/src/g_game.cpp index 491852ffb..e5b3ce8df 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -296,12 +296,12 @@ CCMD (turn180) CCMD (weapnext) { - SendItemUse = PickNextWeapon (&players[consoleplayer]); + SendItemUse = LocalWeapons.PickNextWeapon (&players[consoleplayer]); } CCMD (weapprev) { - SendItemUse = PickPrevWeapon (&players[consoleplayer]); + SendItemUse = LocalWeapons.PickPrevWeapon (&players[consoleplayer]); } CCMD (invnext) diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index fc56e3047..f98518686 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -7,7 +7,6 @@ #define MAX_MANA 200 -#define MAX_WEAPONS_PER_SLOT 8 #define NUM_WEAPON_SLOTS 10 class player_t; @@ -17,30 +16,39 @@ class AWeapon; class FWeaponSlot { public: - FWeaponSlot (); - void Clear (); + FWeaponSlot &operator= (const FWeaponSlot &other) { Weapons = other.Weapons; return *this; } + void Clear() { Weapons.Clear(); } bool AddWeapon (const char *type); bool AddWeapon (const PClass *type); + void AddWeaponList (const char *list, bool clear); AWeapon *PickWeapon (player_t *player); - int CountWeapons (); + int Size () { return (int)Weapons.Size(); } + int LocateWeapon (const PClass *type); inline const PClass *GetWeapon (int index) const { - return Weapons[index]; + if ((unsigned)index < Weapons.Size()) + { + return Weapons[index].Type; + } + else + { + return NULL; + } } - friend AWeapon *PickNextWeapon (player_t *player); - friend AWeapon *PickPrevWeapon (player_t *player); - friend struct FWeaponSlots; private: - const PClass *Weapons[MAX_WEAPONS_PER_SLOT]; + struct WeaponInfo + { + const PClass *Type; + fixed_t Position; + }; + void SetInitialPositions(); + void Sort(); + TArray Weapons; }; - -AWeapon *PickNextWeapon (player_t *player); -AWeapon *PickPrevWeapon (player_t *player); - // FWeaponSlots::AddDefaultWeapon return codes enum ESlotDef { @@ -53,13 +61,20 @@ struct FWeaponSlots { FWeaponSlot Slots[NUM_WEAPON_SLOTS]; + AWeapon *PickNextWeapon (player_t *player); + AWeapon *PickPrevWeapon (player_t *player); + void Clear (); bool LocateWeapon (const PClass *type, int *const slot, int *const index); ESlotDef AddDefaultWeapon (int slot, const PClass *type); - int RestoreSlots (FConfigFile &config); - void SaveSlots (FConfigFile &config); + void AddExtraWeapons(); + void SetFromPlayer(const PClass *type); + void CompleteSetup(const PClass *type); + int RestoreSlots (FConfigFile *config, const char *section); }; +void P_PlaybackKeyConfWeapons(); +void P_SetLocalWeapons(AActor *player); extern FWeaponSlots LocalWeapons; /************************************************************************/ @@ -202,6 +217,13 @@ public: }; // A weapon is just that. +enum +{ + AWMETA_BASE = 0x72000, + AWMETA_SlotNumber, + AWMETA_SlotPriority, +}; + class AWeapon : public AInventory { DECLARE_CLASS (AWeapon, AInventory) diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index 8a13b15a0..22fa776d2 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -25,6 +25,9 @@ IMPLEMENT_POINTY_CLASS (AWeapon) DECLARE_POINTER (SisterWeapon) END_POINTERS +FString WeaponSection; +TArray KeyConfWeapons; + //=========================================================================== // // AWeapon :: Serialize @@ -624,27 +627,22 @@ bool AWeaponGiver::TryPickup(AActor *&toucher) FWeaponSlots LocalWeapons; -FWeaponSlot::FWeaponSlot () -{ - Clear (); -} +//=========================================================================== +// +// FWeaponSlot :: AddWeapon +// +// Adds a weapon to the end of the slot if it isn't already in it. +// +//=========================================================================== -void FWeaponSlot::Clear () -{ - for (int i = 0; i < MAX_WEAPONS_PER_SLOT; i++) - { - Weapons[i] = NULL; - } -} - -bool FWeaponSlot::AddWeapon (const char *type) +bool FWeaponSlot::AddWeapon(const char *type) { return AddWeapon (PClass::FindClass (type)); } -bool FWeaponSlot::AddWeapon (const PClass *type) +bool FWeaponSlot::AddWeapon(const PClass *type) { - int i; + unsigned int i; if (type == NULL) { @@ -657,41 +655,104 @@ bool FWeaponSlot::AddWeapon (const PClass *type) return false; } - for (i = 0; i < MAX_WEAPONS_PER_SLOT; i++) + for (i = 0; i < Weapons.Size(); i++) { - if (Weapons[i] == type) + if (Weapons[i].Type == type) return true; // Already present - if (Weapons[i] == NULL) - break; } - if (i == MAX_WEAPONS_PER_SLOT) - { // This slot is full - return false; - } - Weapons[i] = type; + WeaponInfo info = { type, -1 }; + Weapons.Push(info); return true; } -AWeapon *FWeaponSlot::PickWeapon (player_t *player) +//=========================================================================== +// +// FWeaponSlot :: AddWeaponList +// +// Appends all the weapons from the space-delimited list to this slot. +// Set clear to true to remove any weapons already in this slot first. +// +//=========================================================================== + +void FWeaponSlot :: AddWeaponList(const char *list, bool clear) +{ + FString copy(list); + char *buff = copy.LockBuffer(); + char *tok; + + if (clear) + { + Clear(); + } + tok = strtok(buff, " "); + while (tok != NULL) + { + AddWeapon(tok); + tok = strtok(NULL, " "); + } +} + +//=========================================================================== +// +// FWeaponSlot :: LocateWeapon +// +// Returns the index for the specified weapon in this slot, or -1 if it isn't +// in this slot. +// +//=========================================================================== + +int FWeaponSlot::LocateWeapon(const PClass *type) +{ + unsigned int i; + + for (i = 0; i < Weapons.Size(); ++i) + { + if (Weapons[i].Type == type) + { + return (int)i; + } + } + return -1; +} + +//=========================================================================== +// +// FWeaponSlot :: PickWeapon +// +// Picks a weapon from this slot. If no weapon is selected in this slot, +// or the first weapon in this slot is selected, returns the last weapon. +// Otherwise, returns the previous weapon in this slot. This means +// precedence is given to the last weapon in the slot, which by convention +// is probably the strongest. Does not return weapons you have no ammo +// for or which you do not possess. +// +//=========================================================================== + +AWeapon *FWeaponSlot::PickWeapon(player_t *player) { int i, j; + // Does this slot even have any weapons? + if (Weapons.Size() == 0) + { + return player->ReadyWeapon; + } if (player->ReadyWeapon != NULL) { - for (i = 0; i < MAX_WEAPONS_PER_SLOT; i++) + for (i = 0; i < Weapons.Size(); i++) { - if (Weapons[i] == player->ReadyWeapon->GetClass() || + if (Weapons[i].Type == player->ReadyWeapon->GetClass() || (player->ReadyWeapon->WeaponFlags & WIF_POWERED_UP && player->ReadyWeapon->SisterWeapon != NULL && - player->ReadyWeapon->SisterWeapon->GetClass() == Weapons[i])) + player->ReadyWeapon->SisterWeapon->GetClass() == Weapons[i].Type)) { - for (j = (unsigned)(i - 1) % MAX_WEAPONS_PER_SLOT; + for (j = (unsigned)(i - 1) % Weapons.Size(); j != i; - j = (unsigned)(j - 1) % MAX_WEAPONS_PER_SLOT) + j = (unsigned)(j - 1) % Weapons.Size()) { - AWeapon *weap = static_cast (player->mo->FindInventory (Weapons[j])); + AWeapon *weap = static_cast (player->mo->FindInventory(Weapons[j].Type)); - if (weap != NULL && weap->IsKindOf(RUNTIME_CLASS(AWeapon)) && weap->CheckAmmo (AWeapon::EitherFire, false)) + if (weap != NULL && weap->IsKindOf(RUNTIME_CLASS(AWeapon)) && weap->CheckAmmo(AWeapon::EitherFire, false)) { return weap; } @@ -699,11 +760,11 @@ AWeapon *FWeaponSlot::PickWeapon (player_t *player) } } } - for (i = MAX_WEAPONS_PER_SLOT - 1; i >= 0; i--) + for (i = Weapons.Size() - 1; i >= 0; i--) { - AWeapon *weap = static_cast (player->mo->FindInventory (Weapons[i])); + AWeapon *weap = static_cast (player->mo->FindInventory(Weapons[i].Type)); - if (weap != NULL && weap->IsKindOf(RUNTIME_CLASS(AWeapon)) && weap->CheckAmmo (AWeapon::EitherFire, false)) + if (weap != NULL && weap->IsKindOf(RUNTIME_CLASS(AWeapon)) && weap->CheckAmmo(AWeapon::EitherFire, false)) { return weap; } @@ -711,17 +772,85 @@ AWeapon *FWeaponSlot::PickWeapon (player_t *player) return player->ReadyWeapon; } -void FWeaponSlots::Clear () +//=========================================================================== +// +// FWeaponSlot :: SetInitialPositions +// +// Fills in the position field for every weapon currently in the slot based +// on its position in the slot. These are not scaled to [0,1] so that extra +// weapons can use those values to go to the start or end of the slot. +// +//=========================================================================== + +void FWeaponSlot::SetInitialPositions() { - for (int i = 0; i < NUM_WEAPON_SLOTS; ++i) + unsigned int size = Weapons.Size(), i; + + if (size == 1) { - Slots[i].Clear (); + Weapons[0].Position = 0x8000; + } + else + { + for (i = 0; i < size; ++i) + { + Weapons[i].Position = i * 0xFF00 / (size - 1) + 0x80; + } } } +//=========================================================================== +// +// FWeaponSlot :: Sort +// +// Rearranges the weapons by their position field. +// +//=========================================================================== + +void FWeaponSlot::Sort() +{ + // This does not use qsort(), because the sort should be stable, and + // there is no guarantee that qsort() is stable. This insertion sort + // should be fine. + int i, j; + + for (i = 1; i < (int)Weapons.Size(); ++i) + { + fixed_t pos = Weapons[i].Position; + const PClass *type = Weapons[i].Type; + for (j = i - 1; j >= 0 && Weapons[j].Position > pos; --j) + { + Weapons[j + 1] = Weapons[j]; + } + Weapons[j + 1].Type = type; + Weapons[j + 1].Position = pos; + } +} + +//=========================================================================== +// +// FWeaponSlots :: Clear +// +// Removes all weapons from every slot. +// +//=========================================================================== + +void FWeaponSlots::Clear() +{ + for (int i = 0; i < NUM_WEAPON_SLOTS; ++i) + { + Slots[i].Clear(); + } +} + +//=========================================================================== +// +// FWeaponSlots :: AddDefaultWeapon +// // If the weapon already exists in a slot, don't add it. If it doesn't, -// then add it to the specified slot. False is returned if the weapon was -// not in a slot and could not be added. True is returned otherwise. +// then add it to the specified slot. +// +//=========================================================================== ESlotDef FWeaponSlots::AddDefaultWeapon (int slot, const PClass *type) { @@ -739,43 +868,59 @@ ESlotDef FWeaponSlots::AddDefaultWeapon (int slot, const PClass *type) return SLOTDEF_Exists; } +//=========================================================================== +// +// FWeaponSlots :: LocateWeapon +// +// Returns true if the weapon is in a slot, false otherwise. If the weapon +// is found, it can also optionally return the slot and index for it. +// +//=========================================================================== + bool FWeaponSlots::LocateWeapon (const PClass *type, int *const slot, int *const index) { int i, j; for (i = 0; i < NUM_WEAPON_SLOTS; i++) { - for (j = 0; j < MAX_WEAPONS_PER_SLOT; j++) + j = Slots[i].LocateWeapon(type); + if (j >= 0) { - if (Slots[i].Weapons[j] == type) - { - if (slot != NULL) *slot = i; - if (index != NULL) *index = j; - return true; - } - else if (Slots[i].Weapons[j] == NULL) - { // No more weapons in this slot, so try the next - break; - } + if (slot != NULL) *slot = i; + if (index != NULL) *index = j; + return true; } } return false; } -static bool FindMostRecentWeapon (player_t *player, int *slot, int *index) +//=========================================================================== +// +// FindMostRecentWeapon +// +// Locates the slot and index for the most recently selected weapon. If the +// player is in the process of switching to a new weapon, that is the most +// recently selected weapon. Otherwise, the current weapon is the most recent +// weapon. +// +//=========================================================================== + +static bool FindMostRecentWeapon(player_t *player, int *slot, int *index) { if (player->PendingWeapon != WP_NOCHANGE) { - return LocalWeapons.LocateWeapon (player->PendingWeapon->GetClass(), slot, index); + return LocalWeapons.LocateWeapon(player->PendingWeapon->GetClass(), slot, index); } else if (player->ReadyWeapon != NULL) { AWeapon *weap = player->ReadyWeapon; - if (!LocalWeapons.LocateWeapon (weap->GetClass(), slot, index)) + if (!LocalWeapons.LocateWeapon(weap->GetClass(), slot, index)) { + // If the current weapon wasn't found and is powered up, + // look for its non-powered up version. if (weap->WeaponFlags & WIF_POWERED_UP && weap->SisterWeaponType != NULL) { - return LocalWeapons.LocateWeapon (weap->SisterWeaponType, slot, index); + return LocalWeapons.LocateWeapon(weap->SisterWeaponType, slot, index); } return false; } @@ -787,7 +932,17 @@ static bool FindMostRecentWeapon (player_t *player, int *slot, int *index) } } -AWeapon *PickNextWeapon (player_t *player) +//=========================================================================== +// +// FWeaponSlots :: PickNextWeapon +// +// Returns the "next" weapon for this player. If the current weapon is not +// in a slot, then it just returns that weapon, since there's nothing to +// consider it relative to. +// +//=========================================================================== + +AWeapon *FWeaponSlots::PickNextWeapon(player_t *player) { int startslot, startindex; @@ -795,35 +950,52 @@ AWeapon *PickNextWeapon (player_t *player) { return NULL; } - if (player->ReadyWeapon == NULL || FindMostRecentWeapon (player, &startslot, &startindex)) + if (player->ReadyWeapon == NULL || FindMostRecentWeapon(player, &startslot, &startindex)) { - int start; - int i; + int slot; + int index; if (player->ReadyWeapon == NULL) { startslot = NUM_WEAPON_SLOTS - 1; - startindex = MAX_WEAPONS_PER_SLOT - 1; + startindex = Slots[startslot].Size() - 1; } - start = startslot * MAX_WEAPONS_PER_SLOT + startindex; - for (i = 1; i < NUM_WEAPON_SLOTS * MAX_WEAPONS_PER_SLOT + 1; i++) + slot = startslot; + index = startindex; + do { - int slot = (unsigned)((start + i) / MAX_WEAPONS_PER_SLOT) % NUM_WEAPON_SLOTS; - int index = (unsigned)(start + i) % MAX_WEAPONS_PER_SLOT; - const PClass *type = LocalWeapons.Slots[slot].Weapons[index]; - AWeapon *weap = static_cast (player->mo->FindInventory (type)); - - if (weap != NULL && weap->CheckAmmo (AWeapon::EitherFire, false)) + if (++index >= Slots[slot].Size()) + { + index = 0; + if (++slot >= NUM_WEAPON_SLOTS) + { + slot = 0; + } + } + const PClass *type = LocalWeapons.Slots[slot].GetWeapon(index); + AWeapon *weap = static_cast(player->mo->FindInventory(type)); + if (weap != NULL && weap->CheckAmmo(AWeapon::EitherFire, false)) { return weap; } } + while (slot != startslot || index != startindex); } return player->ReadyWeapon; } -AWeapon *PickPrevWeapon (player_t *player) +//=========================================================================== +// +// FWeaponSlots :: PickPrevWeapon +// +// Returns the "previous" weapon for this player. If the current weapon is +// not in a slot, then it just returns that weapon, since there's nothing to +// consider it relative to. +// +//=========================================================================== + +AWeapon *FWeaponSlots::PickPrevWeapon (player_t *player) { int startslot, startindex; @@ -833,79 +1005,250 @@ AWeapon *PickPrevWeapon (player_t *player) } if (player->ReadyWeapon == NULL || FindMostRecentWeapon (player, &startslot, &startindex)) { - int start; - int i; + int slot; + int index; if (player->ReadyWeapon == NULL) { startslot = 0; startindex = 0; } - start = startslot * MAX_WEAPONS_PER_SLOT + startindex; - for (i = 1; i < NUM_WEAPON_SLOTS * MAX_WEAPONS_PER_SLOT + 1; i++) + slot = startslot; + index = startindex; + do { - int slot = start - i; - if (slot < 0) - slot += NUM_WEAPON_SLOTS * MAX_WEAPONS_PER_SLOT; - int index = slot % MAX_WEAPONS_PER_SLOT; - slot /= MAX_WEAPONS_PER_SLOT; - const PClass *type = LocalWeapons.Slots[slot].Weapons[index]; - AWeapon *weap = static_cast (player->mo->FindInventory (type)); - - if (weap != NULL && weap->CheckAmmo (AWeapon::EitherFire, false)) + if (--index < 0) + { + if (--slot < 0) + { + slot = NUM_WEAPON_SLOTS - 1; + } + index = Slots[slot].Size() - 1; + } + const PClass *type = LocalWeapons.Slots[slot].GetWeapon(index); + AWeapon *weap = static_cast(player->mo->FindInventory(type)); + if (weap != NULL && weap->CheckAmmo(AWeapon::EitherFire, false)) { return weap; } } + while (slot != startslot || index != startindex); } return player->ReadyWeapon; } +//=========================================================================== +// +// FWeaponSlots :: AddExtraWeapons +// +// For every weapon class for the current game, add it to its desired slot +// and position within the slot. Does not first clear the slots. +// +//=========================================================================== + +void FWeaponSlots::AddExtraWeapons() +{ + unsigned int i; + + // Set fractional positions for current weapons. + for (i = 0; i < NUM_WEAPON_SLOTS; ++i) + { + Slots[i].SetInitialPositions(); + } + + // Append extra weapons to the slots. + for (unsigned int i = 0; i < PClass::m_Types.Size(); ++i) + { + PClass *cls = PClass::m_Types[i]; + + if (cls->ActorInfo != NULL && + (cls->ActorInfo->GameFilter == GAME_Any || (cls->ActorInfo->GameFilter & gameinfo.gametype)) && + cls->ActorInfo->Replacement == NULL && // Replaced weapons don't get slotted. + cls->IsDescendantOf(RUNTIME_CLASS(AWeapon)) && + !LocateWeapon(cls, NULL, NULL) // Don't duplicate it if it's already present. + ) + { + int slot = cls->Meta.GetMetaInt(AWMETA_SlotNumber, -1); + if ((unsigned)slot < NUM_WEAPON_SLOTS) + { + fixed_t position = cls->Meta.GetMetaFixed(AWMETA_SlotPriority, INT_MAX); + FWeaponSlot::WeaponInfo info = { cls, position }; + Slots[slot].Weapons.Push(info); + } + } + } + + // Now resort every slot to put the new weapons in their proper places. + for (i = 0; i < NUM_WEAPON_SLOTS; ++i) + { + Slots[i].Sort(); + } +} + +//=========================================================================== +// +// FWeaponSlots :: CompleteSetup +// +// Sets up local weapon slots in this order: +// 1. Use slots from player class. +// 2. Add extra weapons that specify their own slot. +// 3. Run KEYCONF weapon commands, affecting slots accordingly. +// 4. Read config slots, overriding current slots. If WeaponSection is set, +// then [..Weapons] is tried, followed by +// [.Weapons] if that did not exist. If WeaponSection is +// empty, then the slots are read from [.Weapons]. +// +//=========================================================================== + +void FWeaponSlots::CompleteSetup(const PClass *type) +{ + SetFromPlayer(type); + AddExtraWeapons(); + P_PlaybackKeyConfWeapons(); + if (WeaponSection.IsNotEmpty()) + { + FString sectionclass(WeaponSection); + sectionclass << '.' << type->TypeName.GetChars(); + if (RestoreSlots(GameConfig, sectionclass) == 0) + { + RestoreSlots(GameConfig, WeaponSection); + } + } + else + { + RestoreSlots(GameConfig, type->TypeName.GetChars()); + } +} + +//=========================================================================== +// +// FWeaponSlots :: SetFromPlayer +// +// Sets all weapon slots according to the player class. +// +//=========================================================================== + +void FWeaponSlots::SetFromPlayer(const PClass *type) +{ + Clear(); + for (int i = 0; i < NUM_WEAPON_SLOTS; ++i) + { + const char *str = type->Meta.GetMetaString(APMETA_Slot0 + i); + if (str != NULL) + { + Slots[i].AddWeaponList(str, false); + } + } +} + +//=========================================================================== +// +// FWeaponSlots :: RestoreSlots +// +// Reads slots from a config section. Any slots in the section override +// existing slot settings, while slots not present in the config are +// unaffected. Returns the number of slots read. +// +//=========================================================================== + +int FWeaponSlots::RestoreSlots(FConfigFile *config, const char *section) +{ + FString section_name(section); + const char *key, *value; + int slotsread = 0; + + section_name += ".Weapons"; + if (!config->SetSection(section_name)) + { + return 0; + } + while (config->NextInSection (key, value)) + { + if (strnicmp (key, "Slot[", 5) != 0 || + key[5] < '0' || + key[5] > '0'+NUM_WEAPON_SLOTS || + key[6] != ']' || + key[7] != 0) + { + continue; + } + Slots[key[5] - '0'].AddWeaponList(value, true); + slotsread++; + } + return slotsread; +} + +//=========================================================================== +// +// CCMD setslot +// +//=========================================================================== + CCMD (setslot) { - int slot, i; - - if (ParsingKeyConf && WeaponSection.IsEmpty()) - { - Printf ("You need to use weaponsection before using setslot\n"); - return; - } + int slot; if (argv.argc() < 2 || (slot = atoi (argv[1])) >= NUM_WEAPON_SLOTS) { - Printf ("Usage: setslot [slot] [weapons]\nCurrent slot assignments:\n"); - for (slot = 0; slot < NUM_WEAPON_SLOTS; ++slot) + Printf("Usage: setslot [slot] [weapons]\nCurrent slot assignments:\n"); + if (players[consoleplayer].mo != NULL) { - Printf (" Slot %d:", slot); - for (i = 0; - i < MAX_WEAPONS_PER_SLOT && LocalWeapons.Slots[slot].GetWeapon(i) != NULL; - ++i) + FString config(GameConfig->GetConfigPath(false)); + Printf(TEXTCOLOR_BLUE "Add the following to " TEXTCOLOR_ORANGE "%s" TEXTCOLOR_BLUE + " to retain these bindings:\n" TEXTCOLOR_NORMAL "[", config.GetChars()); + if (WeaponSection.IsNotEmpty()) { - Printf (" %s", LocalWeapons.Slots[slot].GetWeapon(i)->TypeName.GetChars()); + Printf("%s.", WeaponSection.GetChars()); + } + Printf("%s.Weapons]\n", players[consoleplayer].mo->GetClass()->TypeName.GetChars()); + } + for (int i = 1; i <= NUM_WEAPON_SLOTS; ++i) + { + int slot = i % NUM_WEAPON_SLOTS; + if (LocalWeapons.Slots[slot].Size() > 0) + { + Printf("Slot[%d]=", slot); + for (int j = 0; j < LocalWeapons.Slots[slot].Size(); ++j) + { + Printf("%s ", LocalWeapons.Slots[slot].GetWeapon(j)->TypeName.GetChars()); + } + Printf("\n"); } - Printf ("\n"); } return; } - LocalWeapons.Slots[slot].Clear(); - if (argv.argc() == 2) + if (ParsingKeyConf) { - Printf ("Slot %d cleared\n", slot); + KeyConfWeapons.Push(argv.args()); } else { - for (i = 2; i < argv.argc(); ++i) + LocalWeapons.Slots[slot].Clear(); + if (argv.argc() == 2) { - if (!LocalWeapons.Slots[slot].AddWeapon (argv[i])) + Printf ("Slot %d cleared\n", slot); + } + else + { + for (int i = 2; i < argv.argc(); ++i) { - Printf ("Could not add %s to slot %d\n", argv[i], slot); + if (!LocalWeapons.Slots[slot].AddWeapon (argv[i])) + { + Printf ("Could not add %s to slot %d\n", argv[i], slot); + } } } } } +//=========================================================================== +// +// CCMD addslot +// +//=========================================================================== + CCMD (addslot) { size_t slot; @@ -916,61 +1259,39 @@ CCMD (addslot) return; } - if (!LocalWeapons.Slots[slot].AddWeapon (argv[2])) + if (ParsingKeyConf) { - Printf ("Could not add %s to slot %zu\n", argv[2], slot); - } -} - -CCMD (weaponsection) -{ - if (argv.argc() != 2) - { - Printf ("Usage: weaponsection \n"); + KeyConfWeapons.Push(argv.args()); } else { - // Limit the section name to 32 chars - if (strlen(argv[1]) > 32) + if (!LocalWeapons.Slots[slot].AddWeapon (argv[2])) { - argv[1][32] = 0; - } - WeaponSection = argv[1]; - - // If the ini already has definitions for this section, load them - char fullSection[32*3]; - char *tackOn; - - if (gameinfo.gametype == GAME_Hexen) - { - strcpy (fullSection, "Hexen"); - tackOn = fullSection + 5; - } - else if (gameinfo.gametype == GAME_Heretic) - { - strcpy (fullSection, "Heretic"); - tackOn = fullSection + 7; - } - else if (gameinfo.gametype == GAME_Strife) - { - strcpy (fullSection, "Strife"); - tackOn = fullSection + 6; - } - else - { - strcpy (fullSection, "Doom"); - tackOn = fullSection + 4; - } - - mysnprintf (tackOn, countof(fullSection) - (tackOn - fullSection), - ".%s.WeaponSlots", WeaponSection.GetChars()); - if (GameConfig->SetSection (fullSection)) - { - LocalWeapons.RestoreSlots (*GameConfig); + Printf ("Could not add %s to slot %zu\n", argv[2], slot); } } } +//=========================================================================== +// +// CCMD weaponsection +// +//=========================================================================== + +CCMD (weaponsection) +{ + if (argv.argc() > 1) + { + WeaponSection = argv[1]; + } +} + +//=========================================================================== +// +// CCMD addslotdefault +// +//=========================================================================== + CCMD (addslotdefault) { const PClass *type; @@ -982,113 +1303,58 @@ CCMD (addslotdefault) return; } - if (ParsingKeyConf && WeaponSection.IsEmpty()) - { - Printf ("You need to use weaponsection before using addslotdefault\n"); - return; - } - type = PClass::FindClass (argv[2]); if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS(AWeapon))) { Printf ("%s is not a weapon\n", argv[2]); } - switch (LocalWeapons.AddDefaultWeapon (slot, type)) + if (ParsingKeyConf) { - case SLOTDEF_Full: - Printf ("Could not add %s to slot %d\n", argv[2], slot); - break; - - case SLOTDEF_Added: - break; - - case SLOTDEF_Exists: - break; + KeyConfWeapons.Push(argv.args()); } -} - -int FWeaponSlots::RestoreSlots (FConfigFile &config) -{ - char buff[MAX_WEAPONS_PER_SLOT*64]; - const char *key, *value; - int slot; - int slotsread = 0; - - buff[sizeof(buff)-1] = 0; - - for (slot = 0; slot < NUM_WEAPON_SLOTS; ++slot) + else { - Slots[slot].Clear (); - } - - while (config.NextInSection (key, value)) - { - if (strnicmp (key, "Slot[", 5) != 0 || - key[5] < '0' || - key[5] > '0'+NUM_WEAPON_SLOTS || - key[6] != ']' || - key[7] != 0) + switch (LocalWeapons.AddDefaultWeapon (slot, type)) { - continue; - } - slot = key[5] - '0'; - strncpy (buff, value, sizeof(buff)-1); - char *tok; + case SLOTDEF_Full: + Printf ("Could not add %s to slot %d\n", argv[2], slot); + break; - Slots[slot].Clear (); - tok = strtok (buff, " "); - while (tok != NULL) - { - Slots[slot].AddWeapon (tok); - tok = strtok (NULL, " "); - } - slotsread++; - } - return slotsread; -} + case SLOTDEF_Added: + break; -void FWeaponSlots::SaveSlots (FConfigFile &config) -{ - char buff[MAX_WEAPONS_PER_SLOT*64]; - char keyname[16]; - - for (int i = 0; i < NUM_WEAPON_SLOTS; ++i) - { - int index = 0; - - for (int j = 0; j < MAX_WEAPONS_PER_SLOT; ++j) - { - if (Slots[i].Weapons[j] == NULL) - { - break; - } - if (index > 0) - { - buff[index++] = ' '; - } - const char *name = Slots[i].Weapons[j]->TypeName.GetChars(); - strcpy (buff+index, name); - index += (int)strlen (name); - } - if (index > 0) - { - mysnprintf (keyname, countof(keyname), "Slot[%d]", i); - config.SetValueForKey (keyname, buff); - } - } -} - -int FWeaponSlot::CountWeapons () -{ - int i; - - for (i = 0; i < MAX_WEAPONS_PER_SLOT; ++i) - { - if (Weapons[i] == NULL) - { + case SLOTDEF_Exists: break; } } - return i; +} + +//=========================================================================== +// +// P_PlaybackKeyConfWeapons +// +// Executes the weapon-related commands from a KEYCONF lump. +// +//=========================================================================== + +void P_PlaybackKeyConfWeapons() +{ + for (unsigned int i = 0; i < KeyConfWeapons.Size(); ++i) + { + FString cmd(KeyConfWeapons[i]); + AddCommandString(cmd.LockBuffer()); + } +} + +//=========================================================================== +// +// CCMD dumpslots +// +// Dumps a config-friendly listing of the current slot assignments. +// +//=========================================================================== + +CCMD (dumpslots) +{ } diff --git a/src/g_shared/sbarinfo_display.cpp b/src/g_shared/sbarinfo_display.cpp index 176241d38..28a1363bb 100644 --- a/src/g_shared/sbarinfo_display.cpp +++ b/src/g_shared/sbarinfo_display.cpp @@ -414,7 +414,7 @@ void DSBarInfo::doCommands(SBarInfoBlock &block, int xOffset, int yOffset, int a if((cmd.flags & DRAWIMAGE_WEAPONSLOT)) //weaponslots { drawAlt = 1; //draw off state until we know we have something. - for (int i = 0; i < MAX_WEAPONS_PER_SLOT; i++) + for (int i = 0; i < LocalWeapons.Slots[cmd.value].Size(); i++) { const PClass *weap = LocalWeapons.Slots[cmd.value].GetWeapon(i); if(weap == NULL) diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 43b060023..3a6271fa0 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -506,7 +506,7 @@ static int DrawAmmo(player_t * CPlayer, int x, int y) // Do not check for actual presence in the inventory! // We want to show all ammo types that can be used by // the weapons in the weapon slots. - for (k=0;k=0;k--) for(j=MAX_WEAPONS_PER_SLOT-1;j>=0;j--) + for (k=NUM_WEAPON_SLOTS-1;k>=0;k--) for(j=LocalWeapons.Slots[k].Size()-1;j>=0;j--) { const PClass * weap = LocalWeapons.Slots[k].GetWeapon(j); if (weap) diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index 9f34c22b8..abb3adde9 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -73,8 +73,6 @@ EXTERN_CVAR (Color, am_cdwallcolor) EXTERN_CVAR (Float, spc_amp) EXTERN_CVAR (Bool, wi_percents) -FString WeaponSection; - FGameConfigFile::FGameConfigFile () { FString pathname; @@ -222,16 +220,6 @@ void FGameConfigFile::DoGlobalSetup () noblitter->ResetToDefault (); } } - if (last < 201) - { - // Be sure the Hexen fourth weapons are assigned to slot 4 - // If this section does not already exist, then they will be - // assigned by SetupWeaponList(). - if (SetSection ("Hexen.WeaponSlots")) - { - SetValueForKey ("Slot[4]", "FWeapQuietus CWeapWraithverge MWeapBloodscourge"); - } - } if (last < 202) { // Make sure the Hexen hotkeys are accessible by default. @@ -271,6 +259,28 @@ void FGameConfigFile::DoGlobalSetup () precache->ResetToDefault(); } } + if (last < 208) + { // Weapon sections are no longer used, so tidy up the config by deleting them. + const char *name; + size_t namelen; + bool more; + + more = SetFirstSection(); + while (more) + { + name = GetCurrentSection(); + if (name != NULL && + (namelen = strlen(name)) > 12 && + strcmp(name + namelen - 12, ".WeaponSlots") == 0) + { + more = DeleteCurrentSection(); + } + else + { + more = SetNextSection(); + } + } + } } } } @@ -376,17 +386,6 @@ void FGameConfigFile::DoGameSetup (const char *gamename) } } -// Separated from DoGameSetup because it needs all the weapons properly defined -void FGameConfigFile::DoWeaponSetup (const char *gamename) -{ - strncpy (subsection, "WeaponSlots", sublen); - - if (!SetSection (section) || !LocalWeapons.RestoreSlots (*this)) - { - SetupWeaponList (gamename); - } -} - void FGameConfigFile::ReadNetVars () { strncpy (subsection, "NetServerInfo", sublen); @@ -460,18 +459,6 @@ void FGameConfigFile::ArchiveGameData (const char *gamename) strncpy (subsection, "DoubleBindings", sublen); SetSection (section, true); C_ArchiveBindings (this, true); - - if (WeaponSection.IsEmpty()) - { - strncpy (subsection, "WeaponSlots", sublen); - } - else - { - mysnprintf (subsection, sublen, "%s.WeaponSlots", WeaponSection.GetChars()); - } - SetSection (section, true); - ClearCurrentSection (); - LocalWeapons.SaveSlots (*this); } void FGameConfigFile::ArchiveGlobalData () @@ -676,79 +663,6 @@ void FGameConfigFile::SetRavenDefaults (bool isHexen) } } -void FGameConfigFile::SetupWeaponList (const char *gamename) -{ - for (int i = 0; i < NUM_WEAPON_SLOTS; ++i) - { - LocalWeapons.Slots[i].Clear (); - } - - if (strcmp (gamename, "Heretic") == 0) - { - LocalWeapons.Slots[1].AddWeapon ("Staff"); - LocalWeapons.Slots[1].AddWeapon ("Gauntlets"); - LocalWeapons.Slots[2].AddWeapon ("GoldWand"); - LocalWeapons.Slots[3].AddWeapon ("Crossbow"); - LocalWeapons.Slots[4].AddWeapon ("Blaster"); - LocalWeapons.Slots[5].AddWeapon ("SkullRod"); - LocalWeapons.Slots[6].AddWeapon ("PhoenixRod"); - LocalWeapons.Slots[7].AddWeapon ("Mace"); - } - else if (strcmp (gamename, "Hexen") == 0) - { - LocalWeapons.Slots[1].AddWeapon ("FWeapFist"); - LocalWeapons.Slots[2].AddWeapon ("FWeapAxe"); - LocalWeapons.Slots[3].AddWeapon ("FWeapHammer"); - LocalWeapons.Slots[4].AddWeapon ("FWeapQuietus"); - LocalWeapons.Slots[1].AddWeapon ("CWeapMace"); - LocalWeapons.Slots[2].AddWeapon ("CWeapStaff"); - LocalWeapons.Slots[3].AddWeapon ("CWeapFlame"); - LocalWeapons.Slots[4].AddWeapon ("CWeapWraithverge"); - LocalWeapons.Slots[1].AddWeapon ("MWeapWand"); - LocalWeapons.Slots[2].AddWeapon ("MWeapFrost"); - LocalWeapons.Slots[3].AddWeapon ("MWeapLightning"); - LocalWeapons.Slots[4].AddWeapon ("MWeapBloodscourge"); - } - else if (strcmp (gamename, "Strife") == 0) - { - LocalWeapons.Slots[1].AddWeapon ("PunchDagger"); - LocalWeapons.Slots[2].AddWeapon ("StrifeCrossbow2"); - LocalWeapons.Slots[2].AddWeapon ("StrifeCrossbow"); - LocalWeapons.Slots[3].AddWeapon ("AssaultGun"); - LocalWeapons.Slots[4].AddWeapon ("MiniMissileLauncher"); - LocalWeapons.Slots[5].AddWeapon ("StrifeGrenadeLauncher2"); - LocalWeapons.Slots[5].AddWeapon ("StrifeGrenadeLauncher"); - LocalWeapons.Slots[6].AddWeapon ("FlameThrower"); - LocalWeapons.Slots[7].AddWeapon ("Mauler2"); - LocalWeapons.Slots[7].AddWeapon ("Mauler"); - LocalWeapons.Slots[8].AddWeapon ("Sigil"); - } - else if (strcmp (gamename, "Chex") == 0) - { - LocalWeapons.Slots[1].AddWeapon ("Bootspoon"); - LocalWeapons.Slots[1].AddWeapon ("SuperBootspork"); - LocalWeapons.Slots[2].AddWeapon ("MiniZorcher"); - LocalWeapons.Slots[3].AddWeapon ("LargeZorcher"); - LocalWeapons.Slots[3].AddWeapon ("SuperLargeZorcher"); - LocalWeapons.Slots[4].AddWeapon ("RapidZorcher"); - LocalWeapons.Slots[5].AddWeapon ("ZorchPropulsor"); - LocalWeapons.Slots[6].AddWeapon ("PhasingZorcher"); - LocalWeapons.Slots[7].AddWeapon ("LAZDevice"); - } - else // Doom - { - LocalWeapons.Slots[1].AddWeapon ("Fist"); - LocalWeapons.Slots[1].AddWeapon ("Chainsaw"); - LocalWeapons.Slots[2].AddWeapon ("Pistol"); - LocalWeapons.Slots[3].AddWeapon ("Shotgun"); - LocalWeapons.Slots[3].AddWeapon ("SuperShotgun"); - LocalWeapons.Slots[4].AddWeapon ("Chaingun"); - LocalWeapons.Slots[5].AddWeapon ("RocketLauncher"); - LocalWeapons.Slots[6].AddWeapon ("PlasmaRifle"); - LocalWeapons.Slots[7].AddWeapon ("BFG9000"); - } -} - CCMD (whereisini) { FString path = GameConfig->GetConfigPath (false); diff --git a/src/gameconfigfile.h b/src/gameconfigfile.h index f4ba727ac..440d900da 100644 --- a/src/gameconfigfile.h +++ b/src/gameconfigfile.h @@ -47,7 +47,6 @@ public: void DoGlobalSetup (); void DoGameSetup (const char *gamename); - void DoWeaponSetup (const char *gamename); void ArchiveGlobalData (); void ArchiveGameData (const char *gamename); void AddAutoexec (DArgs *list, const char *gamename); @@ -64,7 +63,6 @@ private: void MigrateOldConfig (); void SetRavenDefaults (bool isHexen); void ReadCVars (DWORD flags); - void SetupWeaponList (const char *gamename); bool bMigrating; @@ -73,7 +71,6 @@ private: size_t sublen; }; -extern FString WeaponSection; extern FGameConfigFile *GameConfig; #endif //__GAMECONFIGFILE_H__ diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index b8b73aa6d..f8141e1a1 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -737,7 +737,7 @@ void cht_Give (player_t *player, const char *name, int amount) // Give the weapon only if it belongs to the current game or // is in a weapon slot. Unfortunately this check only works in // singleplayer games because the weapon slots are stored locally. - // In multiplayer games or demors all weapons must be given because the state of + // In multiplayer games or demos all weapons must be given because the state of // the weapon slots is not guaranteed to be the same when recording or playing back. if (multiplayer || demorecording || demoplayback || type->ActorInfo->GameFilter == GAME_Any || diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 3b1e93f5c..1370cd9b7 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3769,6 +3769,10 @@ APlayerPawn *P_SpawnPlayer (FMapThing *mthing, bool tempplayer) FBehavior::StaticStartTypedScripts (SCRIPT_Respawn, p->mo, true); } } + if (playernum == consoleplayer) + { + LocalWeapons.CompleteSetup(mobj->GetClass()); + } return mobj; } diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 82b0d22be..67233b391 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1405,6 +1405,24 @@ DEFINE_CLASS_PROPERTY(yadjust, F, Weapon) defaults->YAdjust = i; } +//========================================================================== +// +//========================================================================== +DEFINE_CLASS_PROPERTY(slotnumber, I, Weapon) +{ + PROP_INT_PARM(i, 0); + info->Class->Meta.SetMetaInt(AWMETA_SlotNumber, i); +} + +//========================================================================== +// +//========================================================================== +DEFINE_CLASS_PROPERTY(slotpriority, F, Weapon) +{ + PROP_FIXED_PARM(i, 0); + info->Class->Meta.SetMetaFixed(AWMETA_SlotPriority, i); +} + //========================================================================== // //========================================================================== @@ -1823,6 +1841,30 @@ DEFINE_CLASS_PROPERTY_PREFIX(player, hexenarmor, FFFFF, PlayerPawn) } } +//========================================================================== +// +//========================================================================== +DEFINE_CLASS_PROPERTY_PREFIX(player, weaponslot, ISsssssssssssssssssssssssssssssssssssssssssss, PlayerPawn) +{ + PROP_INT_PARM(slot, 0); + + if (slot < 0 || slot > 9) + { + I_Error("Slot must be between 0 and 9."); + } + else + { + FString weapons; + + for(int i = 1; i < PROP_PARM_COUNT; ++i) + { + PROP_STRING_PARM(str, i); + weapons << ' ' << str; + } + info->Class->Meta.SetMetaString(APMETA_Slot0 + slot, &weapons[1]); + } +} + //========================================================================== // //========================================================================== diff --git a/src/v_font.cpp b/src/v_font.cpp index b38080b02..5e80ee563 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -215,6 +215,8 @@ static int STACK_ARGS TranslationMapCompare (const void *a, const void *b); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- +extern int PrintColors[]; + // PUBLIC DATA DEFINITIONS ------------------------------------------------- FFont *FFont::FirstFont = NULL; @@ -2057,7 +2059,7 @@ EColorRange V_ParseFontColor (const BYTE *&color_value, int normalcolor, int bol const BYTE *ch = color_value; int newcolor = *ch++; - if (newcolor == '-') // Normal + if (newcolor == '-') // Normal { newcolor = normalcolor; } @@ -2065,6 +2067,14 @@ EColorRange V_ParseFontColor (const BYTE *&color_value, int normalcolor, int bol { newcolor = boldcolor; } + else if (newcolor == '!') // Team chat + { + newcolor = PrintColors[PRINT_TEAMCHAT]; + } + else if (newcolor == '*') // Chat + { + newcolor = PrintColors[PRINT_CHAT]; + } else if (newcolor == '[') // Named { const BYTE *namestart = ch; diff --git a/src/v_text.h b/src/v_text.h index 3b9ca951c..494ebb315 100644 --- a/src/v_text.h +++ b/src/v_text.h @@ -71,6 +71,9 @@ struct FBrokenLines #define TEXTCOLOR_NORMAL "\034-" #define TEXTCOLOR_BOLD "\034+" +#define TEXTCOLOR_CHAT "\034*" +#define TEXTCOLOR_TEAMCHAT "\034!" + FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *str); void V_FreeBrokenLines (FBrokenLines *lines); inline FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const char *str) diff --git a/src/version.h b/src/version.h index ad56dad54..10591266d 100644 --- a/src/version.h +++ b/src/version.h @@ -59,7 +59,7 @@ // Version stored in the ini's [LastRun] section. // Bump it if you made some configuration change that you want to // be able to migrate in FGameConfigFile::DoGlobalSetup(). -#define LASTRUNVERSION "207" +#define LASTRUNVERSION "208" // Protocol version used in demos. // Bump it if you change existing DEM_ commands or add new ones. diff --git a/wadsrc/static/actors/chex/chexplayer.txt b/wadsrc/static/actors/chex/chexplayer.txt index 894e5cfc2..5003ea809 100644 --- a/wadsrc/static/actors/chex/chexplayer.txt +++ b/wadsrc/static/actors/chex/chexplayer.txt @@ -9,4 +9,11 @@ actor ChexPlayer : DoomPlayer player.startitem "Bootspoon" player.startitem "MiniZorchRecharge", 50 player.damagescreencolor "60 b0 58" + player.WeaponSlot 1, Bootspoon, SuperBootspork + player.WeaponSlot 2, MiniZorcher + player.WeaponSlot 3, LargeZorcher, SuperLargeZorcher + player.WeaponSlot 4, RapidZorcher + player.WeaponSlot 5, ZorchPropulsor + player.WeaponSlot 6, PhasingZorcher + player.WeaponSlot 7, LAZDevice } diff --git a/wadsrc/static/actors/doom/doomplayer.txt b/wadsrc/static/actors/doom/doomplayer.txt index fcdc1d396..f77fb9ada 100644 --- a/wadsrc/static/actors/doom/doomplayer.txt +++ b/wadsrc/static/actors/doom/doomplayer.txt @@ -17,7 +17,14 @@ ACTOR DoomPlayer : PlayerPawn Player.StartItem "Pistol" Player.StartItem "Fist" Player.StartItem "Clip", 50 - + Player.WeaponSlot 1, Fist, Chainsaw + Player.WeaponSlot 2, Pistol + Player.WeaponSlot 3, Shotgun, SuperShotgun + Player.WeaponSlot 4, Chaingun + Player.WeaponSlot 5, RocketLauncher + Player.WeaponSlot 6, PlasmaRifle + Player.WeaponSlot 7, BFG9000 + States { Spawn: diff --git a/wadsrc/static/actors/doom/doomweapons.txt b/wadsrc/static/actors/doom/doomweapons.txt index aff27e458..3abab7dbf 100644 --- a/wadsrc/static/actors/doom/doomweapons.txt +++ b/wadsrc/static/actors/doom/doomweapons.txt @@ -17,6 +17,7 @@ ACTOR DoomWeapon : Weapon ACTOR Fist : Weapon { + Game Doom Weapon.SelectionOrder 3700 Weapon.Kickback 100 Obituary "$OB_MPFIST" @@ -52,6 +53,7 @@ ACTOR Fist : Weapon ACTOR Pistol : DoomWeapon 5010 { + Game Doom Weapon.SelectionOrder 1900 Weapon.AmmoUse 1 Weapon.AmmoGive 20 diff --git a/wadsrc/static/actors/heretic/hereticplayer.txt b/wadsrc/static/actors/heretic/hereticplayer.txt index fa7dc96e8..9e36f8d7f 100644 --- a/wadsrc/static/actors/heretic/hereticplayer.txt +++ b/wadsrc/static/actors/heretic/hereticplayer.txt @@ -11,6 +11,14 @@ ACTOR HereticPlayer : PlayerPawn Player.StartItem "GoldWand" Player.StartItem "Staff" Player.StartItem "GoldWandAmmo", 50 + Player.WeaponSlot 1, Staff, Gauntlets + Player.WeaponSlot 2, GoldWand + Player.WeaponSlot 3, Crossbow + Player.WeaponSlot 4, Blaster + Player.WeaponSlot 5, SkullRod + Player.WeaponSlot 6, PhoenixRod + Player.WeaponSlot 7, Mace + States { Spawn: diff --git a/wadsrc/static/actors/heretic/hereticweaps.txt b/wadsrc/static/actors/heretic/hereticweaps.txt index 9d558cad2..7b7249ba5 100644 --- a/wadsrc/static/actors/heretic/hereticweaps.txt +++ b/wadsrc/static/actors/heretic/hereticweaps.txt @@ -9,6 +9,7 @@ ACTOR HereticWeapon : Weapon ACTOR Staff : HereticWeapon { + Game Heretic Weapon.SelectionOrder 3800 +THRUGHOST +WIMPY_WEAPON @@ -38,6 +39,7 @@ ACTOR Staff : HereticWeapon ACTOR StaffPowered : Staff { + Game Heretic Weapon.sisterweapon "Staff" Weapon.ReadySound "weapons/staffcrackle" +WEAPON.POWERED_UP @@ -106,6 +108,7 @@ ACTOR StaffPuff2 ACTOR GoldWand : HereticWeapon { + Game Heretic +BLOODSPLATTER Weapon.SelectionOrder 2000 Weapon.AmmoGive 25 @@ -138,6 +141,7 @@ ACTOR GoldWand : HereticWeapon ACTOR GoldWandPowered : GoldWand { + Game Heretic +WEAPON.POWERED_UP Weapon.AmmoGive 0 Weapon.SisterWeapon "GoldWand" @@ -269,6 +273,7 @@ ACTOR Crossbow : HereticWeapon 2001 ACTOR CrossbowPowered : Crossbow { + Game Heretic +WEAPON.POWERED_UP Weapon.AmmoGive 0 Weapon.SisterWeapon "Crossbow" @@ -388,7 +393,7 @@ ACTOR Gauntlets : Weapon 2005 Weapon.YAdjust 15 Weapon.UpSound "weapons/gauntletsactivate" Weapon.SisterWeapon "GauntletsPowered" - Inventory.PickupMessage "$TxT_WPNGAUNTLETS" + Inventory.PickupMessage "$TXT_WPNGAUNTLETS" action native A_GauntletAttack (int power); @@ -420,6 +425,7 @@ ACTOR Gauntlets : Weapon 2005 ACTOR GauntletsPowered : Gauntlets { + Game Heretic +POWERED_UP Weapon.SisterWeapon "Gauntlets" States @@ -518,6 +524,7 @@ ACTOR Mace : HereticWeapon ACTOR MacePowered : Mace { + Game Heretic +WEAPON.POWERED_UP Weapon.AmmoUse 5 Weapon.AmmoGive 0 @@ -704,6 +711,7 @@ ACTOR Blaster : HereticWeapon 53 ACTOR BlasterPowered : Blaster { + Game Heretic +WEAPON.POWERED_UP Weapon.AmmoUse 5 Weapon.AmmoGive 0 @@ -850,6 +858,7 @@ ACTOR SkullRod : HereticWeapon 2004 ACTOR SkullRodPowered : SkullRod { + Game Heretic +WEAPON.POWERED_UP Weapon.AmmoUse1 5 Weapon.AmmoGive1 0 @@ -1022,6 +1031,7 @@ ACTOR PhoenixRod : Weapon 2003 native ACTOR PhoenixRodPowered : PhoenixRod native { + Game Heretic +WEAPON.POWERED_UP Weapon.SisterWeapon "PhoenixRod" Weapon.AmmoGive 0 diff --git a/wadsrc/static/actors/hexen/clericholy.txt b/wadsrc/static/actors/hexen/clericholy.txt index 41224024a..0c441726d 100644 --- a/wadsrc/static/actors/hexen/clericholy.txt +++ b/wadsrc/static/actors/hexen/clericholy.txt @@ -71,6 +71,7 @@ ACTOR WraithvergeDrop ACTOR CWeapWraithverge : ClericWeapon native { + Game Hexen Health 3 Weapon.SelectionOrder 3000 +WEAPON.PRIMARY_USES_BOTH diff --git a/wadsrc/static/actors/hexen/clericmace.txt b/wadsrc/static/actors/hexen/clericmace.txt index a45d9da02..ae9d3b84a 100644 --- a/wadsrc/static/actors/hexen/clericmace.txt +++ b/wadsrc/static/actors/hexen/clericmace.txt @@ -3,6 +3,7 @@ ACTOR CWeapMace : ClericWeapon { + Game Hexen Weapon.SelectionOrder 3500 Weapon.KickBack 150 Weapon.YAdjust -8 diff --git a/wadsrc/static/actors/hexen/clericplayer.txt b/wadsrc/static/actors/hexen/clericplayer.txt index 6300a59a1..2edb06779 100644 --- a/wadsrc/static/actors/hexen/clericplayer.txt +++ b/wadsrc/static/actors/hexen/clericplayer.txt @@ -22,6 +22,11 @@ ACTOR ClericPlayer : PlayerPawn Player.HealRadiusType "Health" Player.Hexenarmor 10, 10, 25, 5, 20 Player.StartItem "CWeapMace" + Player.WeaponSlot 1, CWeapMace + Player.WeaponSlot 2, CWeapStaff + Player.WeaponSlot 3, CWeapFlame + Player.WeaponSlot 4, CWeapWraithverge + States { Spawn: diff --git a/wadsrc/static/actors/hexen/fighterfist.txt b/wadsrc/static/actors/hexen/fighterfist.txt index b67f53124..6693b79bf 100644 --- a/wadsrc/static/actors/hexen/fighterfist.txt +++ b/wadsrc/static/actors/hexen/fighterfist.txt @@ -3,6 +3,7 @@ ACTOR FWeapFist : FighterWeapon { + Game Hexen +BLOODSPLATTER Weapon.SelectionOrder 3400 +WEAPON.MELEEWEAPON diff --git a/wadsrc/static/actors/hexen/fighterplayer.txt b/wadsrc/static/actors/hexen/fighterplayer.txt index be3ff52a5..b824bdb35 100644 --- a/wadsrc/static/actors/hexen/fighterplayer.txt +++ b/wadsrc/static/actors/hexen/fighterplayer.txt @@ -22,6 +22,11 @@ ACTOR FighterPlayer : PlayerPawn Player.StartItem "FWeapFist" Player.ForwardMove 1.08, 1.2 Player.SideMove 1.125, 1.475 + Player.WeaponSlot 1, FWeapFist + Player.WeaponSlot 2, FWeapAxe + Player.WeaponSlot 3, FWeapHammer + Player.WeaponSlot 4, FWeapQuietus + States { Spawn: diff --git a/wadsrc/static/actors/hexen/fighterquietus.txt b/wadsrc/static/actors/hexen/fighterquietus.txt index 176796219..3bd5904f7 100644 --- a/wadsrc/static/actors/hexen/fighterquietus.txt +++ b/wadsrc/static/actors/hexen/fighterquietus.txt @@ -71,6 +71,7 @@ ACTOR QuietusDrop ACTOR FWeapQuietus : FighterWeapon { + Game Hexen Health 3 Weapon.SelectionOrder 2900 +WEAPON.PRIMARY_USES_BOTH diff --git a/wadsrc/static/actors/hexen/mageplayer.txt b/wadsrc/static/actors/hexen/mageplayer.txt index 7da293303..72443af5d 100644 --- a/wadsrc/static/actors/hexen/mageplayer.txt +++ b/wadsrc/static/actors/hexen/mageplayer.txt @@ -24,6 +24,11 @@ ACTOR MagePlayer : PlayerPawn Player.StartItem "MWeapWand" Player.ForwardMove 0.88, 0.92 Player.SideMove 0.875, 0.925 + Player.WeaponSlot 1, MWeapWand + Player.WeaponSlot 2, MWeapFrost + Player.WeaponSlot 3, MWeapLightning + Player.WeaponSlot 4, MWeapBloodscourge + States { Spawn: diff --git a/wadsrc/static/actors/hexen/magestaff.txt b/wadsrc/static/actors/hexen/magestaff.txt index 52a18d09b..4b1887f55 100644 --- a/wadsrc/static/actors/hexen/magestaff.txt +++ b/wadsrc/static/actors/hexen/magestaff.txt @@ -71,6 +71,7 @@ ACTOR BloodscourgeDrop ACTOR MWeapBloodscourge : MageWeapon native { + Game Hexen Health 3 Weapon.SelectionOrder 3100 Weapon.AmmoUse1 15 diff --git a/wadsrc/static/actors/hexen/magewand.txt b/wadsrc/static/actors/hexen/magewand.txt index d2f5ba33b..c3608b5d2 100644 --- a/wadsrc/static/actors/hexen/magewand.txt +++ b/wadsrc/static/actors/hexen/magewand.txt @@ -3,9 +3,11 @@ ACTOR MWeapWand : MageWeapon { + Game Hexen Weapon.SelectionOrder 3600 Weapon.KickBack 0 Weapon.YAdjust 9 + States { Select: diff --git a/wadsrc/static/actors/strife/strifeplayer.txt b/wadsrc/static/actors/strife/strifeplayer.txt index c7c5f22fd..adab59c7b 100644 --- a/wadsrc/static/actors/strife/strifeplayer.txt +++ b/wadsrc/static/actors/strife/strifeplayer.txt @@ -12,6 +12,14 @@ ACTOR StrifePlayer : PlayerPawn Player.ColorRange 128, 143 Player.DisplayName "Rebel" Player.StartItem "PunchDagger" + Player.WeaponSlot 1, PunchDagger + Player.WeaponSlot 2, StrifeCrossbow2, StrifeCrossbow + Player.WeaponSlot 3, AssaultGun + Player.WeaponSlot 4, MiniMissileLauncher + Player.WeaponSlot 5, StrifeGrenadeLauncher2, StrifeGrenadeLauncher + Player.WeaponSlot 6, FlameThrower + Player.WeaponSlot 7, Mauler2, Mauler + Player.WeaponSlot 8, Sigil action native A_ItBurnsItBurns(); action native A_CrispyPlayer(); diff --git a/wadsrc/static/actors/strife/strifeweapons.txt b/wadsrc/static/actors/strife/strifeweapons.txt index 87562bcdb..2b4c5639f 100644 --- a/wadsrc/static/actors/strife/strifeweapons.txt +++ b/wadsrc/static/actors/strife/strifeweapons.txt @@ -45,6 +45,7 @@ ACTOR StrifeSpark : StrifePuff ACTOR PunchDagger : StrifeWeapon { + Game Strife Weapon.SelectionOrder 3900 +WEAPON.NOALERT @@ -198,11 +199,13 @@ ACTOR StrifeCrossbow : StrifeWeapon 2001 ACTOR StrifeCrossbow2 : StrifeCrossbow { + Game Strife Weapon.SelectionOrder 2700 Weapon.AmmoUse1 1 Weapon.AmmoGive1 0 Weapon.AmmoType1 "PoisonBolts" Weapon.SisterWeapon "StrifeCrossbow" + States { Ready: @@ -362,6 +365,7 @@ ACTOR MiniMissilePuff : StrifePuff ACTOR MiniMissile { + Game Strife ConversationID 99,-1,-1 Speed 20 Radius 10 @@ -515,6 +519,7 @@ ACTOR Mauler : StrifeWeapon 2004 ACTOR Mauler2 : Mauler { + Game Strife Weapon.SelectionOrder 3300 Weapon.AmmoUse1 30 Weapon.AmmoGive1 0 @@ -626,6 +631,7 @@ ACTOR MaulerTorpedoWave ACTOR HEGrenade { + Game Strife ConversationID 106,-1,-1 Speed 15 Radius 13 @@ -663,6 +669,7 @@ ACTOR HEGrenade ACTOR PhosphorousGrenade { + Game Strife ConversationID 107,-1,-1 Speed 15 Radius 13 @@ -782,6 +789,7 @@ ACTOR StrifeGrenadeLauncher : StrifeWeapon 154 ACTOR StrifeGrenadeLauncher2 : StrifeGrenadeLauncher { + Game Strife Weapon.SelectionOrder 3200 Weapon.AmmoUse1 1 Weapon.AmmoGive1 0