diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 83c59e364..f44d50aad 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,8 @@ February 20, 2009 (Changes by Graf Zahl) +- Bumped netgame, demo and min demo version for the weapon slot changes. +- changed weapon slots to be stored per player. Information is now transmitted + across the network so that all machines know each player's current weapon + configuration so that it can be used by cheats or HUD display routines. - fixed: level_info_t::mapbg was not initialized February 17, 2009 diff --git a/src/d_net.cpp b/src/d_net.cpp index fd092b49e..3400e45da 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2378,6 +2378,40 @@ void Net_DoCommand (int type, BYTE **stream, int player) P_ConversationCommand (player, stream); break; + case DEM_SETSLOT: + { + BYTE playernum = ReadByte(stream); + BYTE slot = ReadByte(stream); + BYTE count = ReadByte(stream); + TArray weapons; + + weapons.Resize(count); + for(int i = 0; i < count; i++) + { + weapons[i] = ReadStringConst(stream); + } + players[playernum].weapons.SetSlot(slot, weapons); + } + break; + + case DEM_ADDSLOT: + { + BYTE playernum = ReadByte(stream); + BYTE slot = ReadByte(stream); + const char *weap = ReadStringConst(stream); + players[playernum].weapons.AddSlot(slot, weap); + } + break; + + case DEM_ADDSLOTDEFAULT: + { + BYTE playernum = ReadByte(stream); + BYTE slot = ReadByte(stream); + const char *weap = ReadStringConst(stream); + players[playernum].weapons.AddSlotDefault(slot, weap); + } + break; + default: I_Error ("Unknown net command: %d", type); break; @@ -2496,6 +2530,22 @@ void Net_SkipCommand (int type, BYTE **stream) } break; + case DEM_SETSLOT: + { + skip = 3; + for(int numweapons = *(*stream + 2); numweapons > 0; numweapons--) + { + skip += strlen ((char *)(*stream + skip)) + 1; + } + } + break; + + case DEM_ADDSLOT: + case DEM_ADDSLOTDEFAULT: + skip = strlen ((char *)(*stream + 2)) + 3; + break; + + default: return; } diff --git a/src/d_player.h b/src/d_player.h index b78ae5a30..709257860 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -362,6 +362,8 @@ public: fixed_t crouchoffset; fixed_t crouchviewdelta; + FWeaponSlots weapons; + // [CW] I moved these here for multiplayer conversation support. TObjPtr ConversationNPC, ConversationPC; angle_t ConversationNPCAngle; diff --git a/src/d_protocol.cpp b/src/d_protocol.cpp index 0508157c1..25008ca1e 100644 --- a/src/d_protocol.cpp +++ b/src/d_protocol.cpp @@ -49,6 +49,13 @@ char *ReadString (BYTE **stream) return copystring (string); } +const char *ReadStringConst(BYTE **stream) +{ + const char *string = *((const char **)stream); + *stream += strlen (string) + 1; + return string; +} + int ReadByte (BYTE **stream) { BYTE v = **stream; diff --git a/src/d_protocol.h b/src/d_protocol.h index cf268c746..9812ca3f2 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -151,6 +151,9 @@ enum EDemoCommand DEM_SUMMON2, // 52 String: Thing to fabricate, WORD: angle offset DEM_SUMMONFRIEND2, // 53 DEM_SUMMONFOE2, // 54 + DEM_ADDSLOTDEFAULT, // 55 + DEM_ADDSLOT, // 56 + DEM_SETSLOT, // 57 }; // The following are implemented by cht_DoCheat in m_cheat.cpp @@ -226,6 +229,7 @@ int ReadWord (BYTE **stream); int ReadLong (BYTE **stream); float ReadFloat (BYTE **stream); char *ReadString (BYTE **stream); +const char *ReadStringConst(BYTE **stream); void WriteByte (BYTE val, BYTE **stream); void WriteWord (short val, BYTE **stream); void WriteLong (int val, BYTE **stream); diff --git a/src/g_game.cpp b/src/g_game.cpp index e5b3ce8df..d78aba115 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -264,7 +264,7 @@ CCMD (slot) if (slot < NUM_WEAPON_SLOTS) { - SendItemUse = LocalWeapons.Slots[slot].PickWeapon (&players[consoleplayer]); + SendItemUse = players[consoleplayer].weapons.Slots[slot].PickWeapon (&players[consoleplayer]); } } } @@ -296,12 +296,12 @@ CCMD (turn180) CCMD (weapnext) { - SendItemUse = LocalWeapons.PickNextWeapon (&players[consoleplayer]); + SendItemUse = players[consoleplayer].weapons.PickNextWeapon (&players[consoleplayer]); } CCMD (weapprev) { - SendItemUse = LocalWeapons.PickPrevWeapon (&players[consoleplayer]); + SendItemUse = players[consoleplayer].weapons.PickPrevWeapon (&players[consoleplayer]); } CCMD (invnext) diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index f98518686..50c65a6bc 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -71,11 +71,16 @@ struct FWeaponSlots void SetFromPlayer(const PClass *type); void CompleteSetup(const PClass *type); int RestoreSlots (FConfigFile *config, const char *section); + void PrintSettings(); + + void SetSlot(int slot, TArray argv); + void AddSlot(int slot, const char *name); + void AddSlotDefault(int slot, const char *name); + }; void P_PlaybackKeyConfWeapons(); -void P_SetLocalWeapons(AActor *player); -extern FWeaponSlots LocalWeapons; +void P_CompleteWeaponSetup(int playernum, const PClass *type); /************************************************************************/ /* Class definitions */ diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index 22fa776d2..400106ef3 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -16,6 +16,7 @@ #include "thingdef/thingdef.h" #include "doomstat.h" #include "g_level.h" +#include "d_net.h" #define BONUSADD 6 @@ -27,6 +28,7 @@ END_POINTERS FString WeaponSection; TArray KeyConfWeapons; +bool PlayingKeyConf; //=========================================================================== // @@ -909,18 +911,18 @@ static bool FindMostRecentWeapon(player_t *player, int *slot, int *index) { if (player->PendingWeapon != WP_NOCHANGE) { - return LocalWeapons.LocateWeapon(player->PendingWeapon->GetClass(), slot, index); + return player->weapons.LocateWeapon(player->PendingWeapon->GetClass(), slot, index); } else if (player->ReadyWeapon != NULL) { AWeapon *weap = player->ReadyWeapon; - if (!LocalWeapons.LocateWeapon(weap->GetClass(), slot, index)) + if (!player->weapons.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 player->weapons.LocateWeapon(weap->SisterWeaponType, slot, index); } return false; } @@ -973,7 +975,7 @@ AWeapon *FWeaponSlots::PickNextWeapon(player_t *player) slot = 0; } } - const PClass *type = LocalWeapons.Slots[slot].GetWeapon(index); + const PClass *type = Slots[slot].GetWeapon(index); AWeapon *weap = static_cast(player->mo->FindInventory(type)); if (weap != NULL && weap->CheckAmmo(AWeapon::EitherFire, false)) { @@ -1026,7 +1028,7 @@ AWeapon *FWeaponSlots::PickPrevWeapon (player_t *player) } index = Slots[slot].Size() - 1; } - const PClass *type = LocalWeapons.Slots[slot].GetWeapon(index); + const PClass *type = Slots[slot].GetWeapon(index); AWeapon *weap = static_cast(player->mo->FindInventory(type)); if (weap != NULL && weap->CheckAmmo(AWeapon::EitherFire, false)) { @@ -1121,6 +1123,25 @@ void FWeaponSlots::CompleteSetup(const PClass *type) } } +void P_CompleteWeaponSetup(int playernum, const PClass *type) +{ + // Set up the weapon slots locally + LocalWeapons.CompleteSetup(type); + // Now transmit them across the network + for (int i = 0; i < NUM_WEAPON_SLOTS; ++i) + { + Net_WriteByte(DEM_SETSLOT); + Net_WriteByte(playernum); + Net_WriteByte(i); + Net_WriteByte(LocalWeapons.Slots[i].Size()); + for(int j = 0; j < LocalWeapons.Slots[i].Size(); j++) + { + const PClass *cls = LocalWeapons.Slots[i].GetWeapon(j); + if (cls != NULL) Net_WriteString(cls->TypeName.GetChars()); + } + } +} + //=========================================================================== // // FWeaponSlots :: SetFromPlayer @@ -1184,6 +1205,35 @@ int FWeaponSlots::RestoreSlots(FConfigFile *config, const char *section) // CCMD setslot // //=========================================================================== +void FWeaponSlots::SetSlot(int slot, TArray argv) +{ + Slots[slot].Clear(); + for (int i = 0; i < argv.Size(); ++i) + { + if (!Slots[slot].AddWeapon (argv[i])) + { + Printf ("Could not add %s to slot %d\n", argv[i], slot); + } + } +} + + +void FWeaponSlots::PrintSettings() +{ + for (int i = 1; i <= NUM_WEAPON_SLOTS; ++i) + { + int slot = i % NUM_WEAPON_SLOTS; + if (Slots[slot].Size() > 0) + { + Printf("Slot[%d]=", slot); + for (int j = 0; j < Slots[slot].Size(); ++j) + { + Printf("%s ", Slots[slot].GetWeapon(j)->TypeName.GetChars()); + } + Printf("\n"); + } + } +} CCMD (setslot) { @@ -1203,19 +1253,7 @@ CCMD (setslot) } 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"); - } - } + players[consoleplayer].weapons.PrintSettings(); return; } @@ -1223,22 +1261,32 @@ CCMD (setslot) { KeyConfWeapons.Push(argv.args()); } + else if (PlayingKeyConf) + { + BYTE count = argv.argc()-2; + TArray weapons; + + weapons.Resize(count); + for(int i = 0; i < count; i++) + { + weapons[i] = argv[i+2]; + } + LocalWeapons.SetSlot(slot, weapons); + } else { - LocalWeapons.Slots[slot].Clear(); if (argv.argc() == 2) { Printf ("Slot %d cleared\n", slot); } - else + + Net_WriteByte(DEM_SETSLOT); + Net_WriteByte(consoleplayer); + Net_WriteByte(slot); + Net_WriteByte(argv.argc()-2); + for (int i = 2; i < argv.argc(); i++) { - for (int i = 2; i < argv.argc(); ++i) - { - if (!LocalWeapons.Slots[slot].AddWeapon (argv[i])) - { - Printf ("Could not add %s to slot %d\n", argv[i], slot); - } - } + Net_WriteString(argv[i]); } } } @@ -1249,9 +1297,17 @@ CCMD (setslot) // //=========================================================================== +void FWeaponSlots::AddSlot(int slot, const char *name) +{ + if (!Slots[slot].AddWeapon (name)) + { + Printf ("Could not add %s to slot %zu\n", name, slot); + } +} + CCMD (addslot) { - size_t slot; + unsigned int slot; if (argv.argc() != 3 || (slot = atoi (argv[1])) >= NUM_WEAPON_SLOTS) { @@ -1263,12 +1319,16 @@ CCMD (addslot) { KeyConfWeapons.Push(argv.args()); } + else if (PlayingKeyConf) + { + LocalWeapons.AddSlot(int(slot), argv[2]); + } else { - if (!LocalWeapons.Slots[slot].AddWeapon (argv[2])) - { - Printf ("Could not add %s to slot %zu\n", argv[2], slot); - } + Net_WriteByte(DEM_ADDSLOT); + Net_WriteByte(consoleplayer); + Net_WriteByte(slot); + Net_WriteString(argv[2]); } } @@ -1291,6 +1351,26 @@ CCMD (weaponsection) // CCMD addslotdefault // //=========================================================================== +void FWeaponSlots::AddSlotDefault(int slot, const char *name) +{ + const PClass *type = PClass::FindClass (name); + if (type != NULL && type->IsDescendantOf (RUNTIME_CLASS(AWeapon))) + { + switch (AddDefaultWeapon (slot, type)) + { + case SLOTDEF_Full: + Printf ("Could not add %s to slot %d\n", name, slot); + break; + + default: + case SLOTDEF_Added: + break; + + case SLOTDEF_Exists: + break; + } + } +} CCMD (addslotdefault) { @@ -1307,26 +1387,23 @@ CCMD (addslotdefault) if (type == NULL || !type->IsDescendantOf (RUNTIME_CLASS(AWeapon))) { Printf ("%s is not a weapon\n", argv[2]); + return; } if (ParsingKeyConf) { KeyConfWeapons.Push(argv.args()); } + else if (PlayingKeyConf) + { + LocalWeapons.AddSlotDefault(int(slot), argv[2]); + } else { - switch (LocalWeapons.AddDefaultWeapon (slot, type)) - { - case SLOTDEF_Full: - Printf ("Could not add %s to slot %d\n", argv[2], slot); - break; - - case SLOTDEF_Added: - break; - - case SLOTDEF_Exists: - break; - } + Net_WriteByte(DEM_ADDSLOTDEFAULT); + Net_WriteByte(consoleplayer); + Net_WriteByte(slot); + Net_WriteString(argv[2]); } } @@ -1340,11 +1417,13 @@ CCMD (addslotdefault) void P_PlaybackKeyConfWeapons() { + PlayingKeyConf = true; for (unsigned int i = 0; i < KeyConfWeapons.Size(); ++i) { FString cmd(KeyConfWeapons[i]); AddCommandString(cmd.LockBuffer()); } + PlayingKeyConf = false; } //=========================================================================== diff --git a/src/g_shared/sbarinfo_display.cpp b/src/g_shared/sbarinfo_display.cpp index 28a1363bb..702e38682 100644 --- a/src/g_shared/sbarinfo_display.cpp +++ b/src/g_shared/sbarinfo_display.cpp @@ -414,9 +414,9 @@ 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 < LocalWeapons.Slots[cmd.value].Size(); i++) + for (int i = 0; i < CPlayer->weapons.Slots[cmd.value].Size(); i++) { - const PClass *weap = LocalWeapons.Slots[cmd.value].GetWeapon(i); + const PClass *weap = CPlayer->weapons.Slots[cmd.value].GetWeapon(i); if(weap == NULL) { continue; diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 3a6271fa0..a1905aff9 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -491,14 +491,14 @@ static void AddAmmoToList(AWeapon * weapdef) } } -static int DrawAmmo(player_t * CPlayer, int x, int y) +static int DrawAmmo(player_t *CPlayer, int x, int y) { int i,j,k; char buf[256]; - AInventory * inv; + AInventory *inv; - AWeapon * wi=CPlayer->ReadyWeapon; + AWeapon *wi=CPlayer->ReadyWeapon; orderedammos.Clear(); @@ -506,9 +506,9 @@ 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;kweapons.Slots[k].Size(); j++) { - const PClass * weap = LocalWeapons.Slots[k].GetWeapon(j); + const PClass *weap = CPlayer->weapons.Slots[k].GetWeapon(j); if (weap) AddAmmoToList((AWeapon*)GetDefaultByType(weap)); } @@ -628,17 +628,17 @@ static void DrawWeapons(player_t * CPlayer, int x, int y) // First draw all weapons in the inventory that are not assigned to a weapon slot for(inv=CPlayer->mo->Inventory;inv;inv=inv->Inventory) { - int slot, index; - if (inv->IsKindOf(RUNTIME_CLASS(AWeapon)) && !LocalWeapons.LocateWeapon(RUNTIME_TYPE(inv), &slot, &index)) + if (inv->IsKindOf(RUNTIME_CLASS(AWeapon)) && + !CPlayer->weapons.LocateWeapon(RUNTIME_TYPE(inv), NULL, NULL)) { DrawOneWeapon(CPlayer, x, y, static_cast(inv)); } } // And now everything in the weapon slots back to front - for (k=NUM_WEAPON_SLOTS-1;k>=0;k--) for(j=LocalWeapons.Slots[k].Size()-1;j>=0;j--) + for (k = NUM_WEAPON_SLOTS - 1; k >= 0; k--) for(j = CPlayer->weapons.Slots[k].Size() - 1; j >= 0; j--) { - const PClass * weap = LocalWeapons.Slots[k].GetWeapon(j); + const PClass *weap = CPlayer->weapons.Slots[k].GetWeapon(j); if (weap) { inv=CPlayer->mo->FindInventory(weap); diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index f8141e1a1..1401cd338 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -735,14 +735,10 @@ 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 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 || + // is in a weapon slot. + if (type->ActorInfo->GameFilter == GAME_Any || (type->ActorInfo->GameFilter & gameinfo.gametype) || - LocalWeapons.LocateWeapon(type, NULL, NULL)) + player->weapons.LocateWeapon(type, NULL, NULL)) { AWeapon *def = (AWeapon*)GetDefaultByType (type); if (!(def->WeaponFlags & WIF_CHEATNOTWEAPON)) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 1370cd9b7..a4b22b175 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3771,7 +3771,7 @@ APlayerPawn *P_SpawnPlayer (FMapThing *mthing, bool tempplayer) } if (playernum == consoleplayer) { - LocalWeapons.CompleteSetup(mobj->GetClass()); + P_CompleteWeaponSetup(consoleplayer, mobj->GetClass()); } return mobj; } diff --git a/src/version.h b/src/version.h index 10591266d..b8ff05a07 100644 --- a/src/version.h +++ b/src/version.h @@ -54,7 +54,7 @@ // Version identifier for network games. // Bump it every time you do a release unless you're certain you // didn't change anything that will affect sync. -#define NETGAMEVERSION 218 +#define NETGAMEVERSION 219 // Version stored in the ini's [LastRun] section. // Bump it if you made some configuration change that you want to @@ -64,11 +64,11 @@ // Protocol version used in demos. // Bump it if you change existing DEM_ commands or add new ones. // Otherwise, it should be safe to leave it alone. -#define DEMOGAMEVERSION 0x20E +#define DEMOGAMEVERSION 0x20F // Minimum demo version we can play. // Bump it whenever you change or remove existing DEM_ commands. -#define MINDEMOVERSION 0x207 +#define MINDEMOVERSION 0x20F // SAVEVER is the version of the information stored in level snapshots. // Note that SAVEVER is not directly comparable to VERSION.