- Added Karate Chris's submission for multiplayer Strife conversations.

SVN r855 (trunk)
This commit is contained in:
Christoph Oelckers 2008-03-26 08:29:02 +00:00
parent 8d0c48bf81
commit a01aaf35ad
10 changed files with 251 additions and 93 deletions

View file

@ -33,6 +33,7 @@ March 25, 2008
the 2D panning area. the 2D panning area.
March 25, 2008 (Changes by Graf Zahl) March 25, 2008 (Changes by Graf Zahl)
- Added Karate Chris's submission for multiplayer Strife conversations.
- Increased the limit for 'imp/active' to 6. This sound definitely benefits - Increased the limit for 'imp/active' to 6. This sound definitely benefits
from a higher limit. from a higher limit.
- Fixed: $limit should not apply to sounds played from the menu. - Fixed: $limit should not apply to sounds played from the menu.

View file

@ -53,6 +53,7 @@
#include "a_sharedglobal.h" #include "a_sharedglobal.h"
#include "st_start.h" #include "st_start.h"
#include "teaminfo.h" #include "teaminfo.h"
#include "p_conversation.h"
int P_StartScript (AActor *who, line_t *where, int script, char *map, bool backSide, int P_StartScript (AActor *who, line_t *where, int script, char *map, bool backSide,
int arg0, int arg1, int arg2, int always, bool wantResultCode, bool net); int arg0, int arg1, int arg2, int always, bool wantResultCode, bool net);
@ -2361,6 +2362,10 @@ void Net_DoCommand (int type, BYTE **stream, int player)
} }
break; break;
case DEM_CONVERSATION:
P_ConversationCommand (player, stream);
break;
default: default:
I_Error ("Unknown net command: %d", type); I_Error ("Unknown net command: %d", type);
break; break;
@ -2451,6 +2456,31 @@ void Net_SkipCommand (int type, BYTE **stream)
skip = 3 + *(*stream + 2) * 4; skip = 3 + *(*stream + 2) * 4;
break; break;
case DEM_CONVERSATION:
{
t = **stream;
skip = 1;
switch (t)
{
case CONV_ANIMATE:
skip += 1;
break;
case CONV_GIVEINVENTORY:
skip += strlen ((char *)(*stream + skip)) + 1;
break;
case CONV_TAKEINVENTORY:
skip += strlen ((char *)(*stream + skip)) + 3;
break;
default:
break;
}
}
break;
default: default:
return; return;
} }

View file

@ -320,6 +320,11 @@ public:
fixed_t crouchoffset; fixed_t crouchoffset;
fixed_t crouchviewdelta; fixed_t crouchviewdelta;
// [CW] I moved these here for multiplayer conversation support.
AActor *ConversationNPC, *ConversationPC;
angle_t ConversationNPCAngle;
bool ConversationFaceTalker;
fixed_t GetDeltaViewHeight() const fixed_t GetDeltaViewHeight() const
{ {
return (mo->ViewHeight + crouchviewdelta - viewheight) >> 3; return (mo->ViewHeight + crouchviewdelta - viewheight) >> 3;

View file

@ -150,6 +150,7 @@ enum EDemoCommand
DEM_ADDCONTROLLER, // 48 Player to add to the controller list. DEM_ADDCONTROLLER, // 48 Player to add to the controller list.
DEM_DELCONTROLLER, // 49 Player to remove from the controller list. DEM_DELCONTROLLER, // 49 Player to remove from the controller list.
DEM_KILLCLASSCHEAT, // 50 String: Class to kill. DEM_KILLCLASSCHEAT, // 50 String: Class to kill.
DEM_CONVERSATION, // 51 Make conversations work.
}; };
// The following are implemented by cht_DoCheat in m_cheat.cpp // The following are implemented by cht_DoCheat in m_cheat.cpp

View file

@ -81,6 +81,13 @@ public:
bool TryPickup (AActor *toucher); bool TryPickup (AActor *toucher);
}; };
class ASlideshowStarter : public ADummyStrifeItem
{
DECLARE_STATELESS_ACTOR (ASlideshowStarter, ADummyStrifeItem)
public:
bool TryPickup (AActor *toucher);
};
class AStrifeWeapon : public AWeapon class AStrifeWeapon : public AWeapon
{ {
DECLARE_STATELESS_ACTOR (AStrifeWeapon, AWeapon) DECLARE_STATELESS_ACTOR (AStrifeWeapon, AWeapon)

View file

@ -514,13 +514,6 @@ bool AUpgradeAccuracy::TryPickup (AActor *toucher)
// Start a slideshow -------------------------------------------------------- // Start a slideshow --------------------------------------------------------
class ASlideshowStarter : public ADummyStrifeItem
{
DECLARE_STATELESS_ACTOR (ASlideshowStarter, ADummyStrifeItem)
public:
bool TryPickup (AActor *toucher);
};
IMPLEMENT_STATELESS_ACTOR (ASlideshowStarter, Strife, -1, 0) IMPLEMENT_STATELESS_ACTOR (ASlideshowStarter, Strife, -1, 0)
PROP_StrifeType (343) PROP_StrifeType (343)
END_DEFAULTS END_DEFAULTS

View file

@ -117,9 +117,8 @@ static void ConversationMenuEscaped ();
static FStrifeDialogueNode *CurNode, *PrevNode; static FStrifeDialogueNode *CurNode, *PrevNode;
static FBrokenLines *DialogueLines; static FBrokenLines *DialogueLines;
static AActor *ConversationNPC, *ConversationPC;
static angle_t ConversationNPCAngle; static bool Conversation_TakeStuff;
static bool ConversationFaceTalker;
#define NUM_RANDOM_LINES 10 #define NUM_RANDOM_LINES 10
#define NUM_RANDOM_GOODBYES 3 #define NUM_RANDOM_GOODBYES 3
@ -571,14 +570,14 @@ static int FindNode (const FStrifeDialogueNode *node)
// //
//============================================================================ //============================================================================
static bool CheckStrifeItem (const PClass *itemtype, int amount=-1) static bool CheckStrifeItem (player_t *player, const PClass *itemtype, int amount=-1)
{ {
AInventory *item; AInventory *item;
if (itemtype == NULL || amount == 0) if (itemtype == NULL || amount == 0)
return true; return true;
item = ConversationPC->FindInventory (itemtype); item = player->ConversationPC->FindInventory (itemtype);
if (item == NULL) if (item == NULL)
return false; return false;
@ -594,7 +593,7 @@ static bool CheckStrifeItem (const PClass *itemtype, int amount=-1)
// //
//============================================================================ //============================================================================
static void TakeStrifeItem (const PClass *itemtype, int amount) static void TakeStrifeItem (player_t *player, const PClass *itemtype, int amount)
{ {
if (itemtype == NULL || amount == 0) if (itemtype == NULL || amount == 0)
return; return;
@ -611,15 +610,10 @@ static void TakeStrifeItem (const PClass *itemtype, int amount)
if (itemtype == RUNTIME_CLASS(ASigil)) if (itemtype == RUNTIME_CLASS(ASigil))
return; return;
AInventory *item = ConversationPC->FindInventory (itemtype); Net_WriteByte (DEM_CONVERSATION);
if (item != NULL) Net_WriteByte (CONV_TAKEINVENTORY);
{ Net_WriteString (itemtype->TypeName.GetChars ());
item->Amount -= amount; Net_WriteWord (amount);
if (item->Amount <= 0)
{
item->Destroy ();
}
}
} }
CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE) CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE)
@ -633,7 +627,6 @@ CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE)
// P_StartConversation // P_StartConversation
// //
// Begins a conversation between a PC and NPC. // Begins a conversation between a PC and NPC.
// FIXME: Make this work in multiplayer.
// //
//============================================================================ //============================================================================
@ -645,14 +638,22 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
const char *toSay; const char *toSay;
int i, j; int i, j;
// [CW] If an NPC is talking to a PC already, then don't let
// anyone else talk to NPC.
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || pc->player == &players[i])
continue;
if (npc == players[i].ConversationNPC)
return;
}
pc->momx = pc->momy = 0; // Stop moving pc->momx = pc->momy = 0; // Stop moving
pc->player->momx = pc->player->momy = 0; pc->player->momx = pc->player->momy = 0;
if (pc->player - players != consoleplayer) pc->player->ConversationPC = pc;
return; pc->player->ConversationNPC = npc;
ConversationPC = pc;
ConversationNPC = npc;
CurNode = npc->Conversation; CurNode = npc->Conversation;
@ -662,10 +663,10 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
} }
npc->reactiontime = 2; npc->reactiontime = 2;
ConversationFaceTalker = facetalker; pc->player->ConversationFaceTalker = facetalker;
if (saveangle) if (saveangle)
{ {
ConversationNPCAngle = npc->angle; pc->player->ConversationNPCAngle = npc->angle;
} }
oldtarget = npc->target; oldtarget = npc->target;
npc->target = pc; npc->target = pc;
@ -682,11 +683,11 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
// Check if we should jump to another node // Check if we should jump to another node
while (CurNode->ItemCheck[0] != NULL) while (CurNode->ItemCheck[0] != NULL)
{ {
if (CheckStrifeItem (CurNode->ItemCheck[0]) && if (CheckStrifeItem (pc->player, CurNode->ItemCheck[0]) &&
CheckStrifeItem (CurNode->ItemCheck[1]) && CheckStrifeItem (pc->player, CurNode->ItemCheck[1]) &&
CheckStrifeItem (CurNode->ItemCheck[2])) CheckStrifeItem (pc->player, CurNode->ItemCheck[2]))
{ {
int root = FindNode (ConversationNPC->GetDefault()->Conversation); int root = FindNode (pc->player->ConversationNPC->GetDefault()->Conversation);
CurNode = StrifeDialogues[root + CurNode->ItemCheckNode - 1]; CurNode = StrifeDialogues[root + CurNode->ItemCheckNode - 1];
} }
else else
@ -697,10 +698,13 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
if (CurNode->SpeakerVoice != 0) if (CurNode->SpeakerVoice != 0)
{ {
I_SetMusicVolume(dlg_musicvolume); I_SetMusicVolume (dlg_musicvolume);
S_SoundID (npc, CHAN_VOICE|CHAN_NOPAUSE, CurNode->SpeakerVoice, 1, ATTN_NORM); S_SoundID (npc, CHAN_VOICE|CHAN_NOPAUSE, CurNode->SpeakerVoice, 1, ATTN_NORM);
} }
if (pc->player != &players[consoleplayer])
return;
// Set up the menu // Set up the menu
ConversationMenu.PreDraw = DrawConversationMenu; ConversationMenu.PreDraw = DrawConversationMenu;
ConversationMenu.EscapeHandler = ConversationMenuEscaped; ConversationMenu.EscapeHandler = ConversationMenuEscaped;
@ -786,9 +790,17 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
void P_ResumeConversation () void P_ResumeConversation ()
{ {
if (ConversationPC != NULL && ConversationNPC != NULL) for (int i = 0; i < MAXPLAYERS; i++)
{ {
P_StartConversation (ConversationNPC, ConversationPC, ConversationFaceTalker, false); if (!playeringame[i])
continue;
player_t *p = &players[i];
if (p->ConversationPC != NULL && p->ConversationNPC != NULL)
{
P_StartConversation (p->ConversationNPC, p->ConversationPC, p->ConversationFaceTalker, false);
}
} }
} }
@ -803,6 +815,8 @@ static void DrawConversationMenu ()
const char *speakerName; const char *speakerName;
int i, x, y, linesize; int i, x, y, linesize;
player_t *cp = &players[consoleplayer];
assert (DialogueLines != NULL); assert (DialogueLines != NULL);
assert (CurNode != NULL); assert (CurNode != NULL);
@ -812,7 +826,8 @@ static void DrawConversationMenu ()
return; return;
} }
if (ConversationPauseTic < gametic) // [CW] Pausing the game in a multiplayer game is a bad idea.
if (ConversationPauseTic < gametic && !multiplayer)
{ {
menuactive = MENU_On; menuactive = MENU_On;
} }
@ -832,7 +847,7 @@ static void DrawConversationMenu ()
} }
else else
{ {
speakerName = ConversationNPC->GetClass()->Meta.GetMetaString (AMETA_StrifeName); speakerName = cp->ConversationNPC->GetClass()->Meta.GetMetaString (AMETA_StrifeName);
if (speakerName == NULL) if (speakerName == NULL)
{ {
speakerName = "Person"; speakerName = "Person";
@ -873,7 +888,7 @@ static void DrawConversationMenu ()
if (ShowGold) if (ShowGold)
{ {
AInventory *coin = ConversationPC->FindInventory (RUNTIME_CLASS(ACoin)); AInventory *coin = cp->ConversationPC->FindInventory (RUNTIME_CLASS(ACoin));
char goldstr[32]; char goldstr[32];
sprintf (goldstr, "%d", coin != NULL ? coin->Amount : 0); sprintf (goldstr, "%d", coin != NULL ? coin->Amount : 0);
@ -892,94 +907,89 @@ static void DrawConversationMenu ()
// //
// PickConversationReply // PickConversationReply
// //
// FIXME: Make this work in multiplayer
//
//============================================================================ //============================================================================
static void PickConversationReply () static void PickConversationReply ()
{ {
const char *replyText = NULL; const char *replyText = NULL;
FStrifeDialogueReply *reply = (FStrifeDialogueReply *)ConversationItems[ConversationMenu.lastOn].c.extra; FStrifeDialogueReply *reply = (FStrifeDialogueReply *)ConversationItems[ConversationMenu.lastOn].c.extra;
bool takestuff;
int i; int i;
player_t *cp = &players[consoleplayer];
Conversation_TakeStuff = false;
M_ClearMenus (); M_ClearMenus ();
CleanupConversationMenu (); CleanupConversationMenu ();
if (reply == NULL) if (reply == NULL)
{ {
ConversationNPC->angle = ConversationNPCAngle; Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_NPCANGLE);
return; return;
} }
// Check if you have the requisite items for this choice // Check if you have the requisite items for this choice
for (i = 0; i < 3; ++i) for (i = 0; i < 3; ++i)
{ {
if (!CheckStrifeItem (reply->ItemCheck[i], reply->ItemCheckAmount[i])) if (!CheckStrifeItem (cp, reply->ItemCheck[i], reply->ItemCheckAmount[i]))
{ {
// No, you don't. Say so and let the NPC animate negatively. // No, you don't. Say so and let the NPC animate negatively.
if (reply->QuickNo) if (reply->QuickNo)
{ {
Printf ("%s\n", reply->QuickNo); Printf ("%s\n", reply->QuickNo);
} }
ConversationNPC->ConversationAnimation (2); Net_WriteByte (DEM_CONVERSATION);
ConversationNPC->angle = ConversationNPCAngle; Net_WriteByte (CONV_ANIMATE);
Net_WriteByte (2);
Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_NPCANGLE);
return; return;
} }
} }
// Yay, you do! Let the NPC animate affirmatively. // Yay, you do! Let the NPC animate affirmatively.
ConversationNPC->ConversationAnimation (1); Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_ANIMATE);
Net_WriteByte (1);
// If this reply gives you something, then try to receive it. // If this reply gives you something, then try to receive it.
takestuff = true; Conversation_TakeStuff = true;
if (reply->GiveType != NULL) if (reply->GiveType != NULL)
{ {
if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(AInventory))) if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(AInventory)))
{ {
if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(AWeapon))) if (reply->GiveType->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{ {
if (players[consoleplayer].mo->FindInventory(reply->GiveType) != NULL) if (cp->mo->FindInventory(reply->GiveType) != NULL)
{ {
takestuff = false; Conversation_TakeStuff = false;
} }
} }
if (takestuff) if (Conversation_TakeStuff)
{ {
AInventory *item = static_cast<AInventory *> (Spawn (reply->GiveType, 0, 0, 0, NO_REPLACE)); Net_WriteByte (DEM_CONVERSATION);
// Items given here should not count as items! Net_WriteByte (CONV_GIVEINVENTORY);
if (item->flags & MF_COUNTITEM) Net_WriteString (reply->GiveType->TypeName.GetChars ());
{
level.total_items--;
item->flags &= ~MF_COUNTITEM;
}
if (item->IsA(RUNTIME_CLASS(AFlameThrower)))
{
// The flame thrower gives less ammo when given in a dialog
static_cast<AWeapon*>(item)->AmmoGive1 = 40;
}
item->flags |= MF_DROPPED;
if (!item->TryPickup (players[consoleplayer].mo))
{
item->Destroy ();
takestuff = false;
}
} }
if (reply->GiveType->IsDescendantOf (RUNTIME_CLASS (ASlideshowStarter)))
gameaction = ga_slideshow;
} }
else else
{ {
// Trying to give a non-inventory item. // Trying to give a non-inventory item.
takestuff = false; Conversation_TakeStuff = false;
Printf("Attempting to give non-inventory item %s\n", reply->GiveType->TypeName.GetChars()); Printf("Attempting to give non-inventory item %s\n", reply->GiveType->TypeName.GetChars());
} }
} }
// Take away required items if the give was successful or none was needed. // Take away required items if the give was successful or none was needed.
if (takestuff) if (Conversation_TakeStuff)
{ {
for (i = 0; i < 3; ++i) for (i = 0; i < 3; ++i)
{ {
TakeStrifeItem (reply->ItemCheck[i], reply->ItemCheckAmount[i]); TakeStrifeItem (&players[consoleplayer], reply->ItemCheck[i], reply->ItemCheckAmount[i]);
} }
replyText = reply->QuickYes; replyText = reply->QuickYes;
} }
@ -991,7 +1001,7 @@ static void PickConversationReply ()
// Update the quest log, if needed. // Update the quest log, if needed.
if (reply->LogNumber != 0) if (reply->LogNumber != 0)
{ {
players[consoleplayer].SetLogNumber (reply->LogNumber); cp->SetLogNumber (reply->LogNumber);
} }
if (replyText != NULL) if (replyText != NULL)
@ -1000,32 +1010,43 @@ static void PickConversationReply ()
} }
// Does this reply alter the speaker's conversation node? If NextNode is positive, // Does this reply alter the speaker's conversation node? If NextNode is positive,
// the next time they talk, the will show the new node. If it is negative, then they // the next time they talk, they will show the new node. If it is negative, then they
// will show the new node right away without terminating the dialogue. // will show the new node right away without terminating the dialogue.
if (reply->NextNode != 0) if (reply->NextNode != 0)
{ {
int rootnode = FindNode (ConversationNPC->GetDefault()->Conversation); int rootnode = FindNode (cp->ConversationNPC->GetDefault()->Conversation);
if (reply->NextNode < 0) if (reply->NextNode < 0)
{ {
ConversationNPC->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1]; cp->ConversationNPC->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1];
if (gameaction != ga_slideshow) if (gameaction != ga_slideshow)
{ {
P_StartConversation (ConversationNPC, players[consoleplayer].mo, ConversationFaceTalker, false); P_StartConversation (cp->ConversationNPC, cp->mo, cp->ConversationFaceTalker, false);
return; return;
} }
else else
{ {
S_StopSound (ConversationNPC, CHAN_VOICE); S_StopSound (cp->ConversationNPC, CHAN_VOICE);
} }
} }
else else
{ {
ConversationNPC->Conversation = StrifeDialogues[rootnode + reply->NextNode - 1]; cp->ConversationNPC->Conversation = StrifeDialogues[rootnode + reply->NextNode - 1];
} }
} }
ConversationNPC->angle = ConversationNPCAngle; Net_WriteByte (DEM_CONVERSATION);
I_SetMusicVolume(1.f); Net_WriteByte (CONV_NPCANGLE);
// [CW] Set these to NULL because we're not talking to them
// anymore. However, this can interfere with slideshows so
// we don't set them to NULL in that case.
if (gameaction != ga_slideshow)
{
Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_SETNULL);
}
I_SetMusicVolume (1.f);
} }
//============================================================================ //============================================================================
@ -1058,19 +1079,96 @@ void CleanupConversationMenu ()
DialogueLines = NULL; DialogueLines = NULL;
} }
ConversationItems.Clear (); ConversationItems.Clear ();
I_SetMusicVolume(1.f); I_SetMusicVolume (1.f);
} }
//============================================================================ //============================================================================
// //
// ConversationMenuEscaped // ConversationMenuEscaped
// //
// Called when the user presses escape to leave tho conversation menu. // Called when the user presses escape to leave the conversation menu.
// //
//============================================================================ //============================================================================
void ConversationMenuEscaped () void ConversationMenuEscaped ()
{ {
CleanupConversationMenu (); CleanupConversationMenu ();
ConversationNPC->angle = ConversationNPCAngle;
Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_NPCANGLE);
Net_WriteByte (DEM_CONVERSATION);
Net_WriteByte (CONV_SETNULL);
}
//============================================================================
//
// P_ConversationCommand
//
// Complete a conversation command.
//
//============================================================================
void P_ConversationCommand (int player, BYTE **stream)
{
int type = ReadByte (stream);
switch (type)
{
case CONV_NPCANGLE:
players[player].ConversationNPC->angle = players[player].ConversationNPCAngle;
break;
case CONV_ANIMATE:
players[player].ConversationNPC->ConversationAnimation (ReadByte (stream));
break;
case CONV_GIVEINVENTORY:
{
AInventory *item = static_cast<AInventory *> (Spawn (ReadString (stream), 0, 0, 0, NO_REPLACE));
// Items given here should not count as items!
if (item->flags & MF_COUNTITEM)
{
level.total_items--;
item->flags &= ~MF_COUNTITEM;
}
if (item->IsA(RUNTIME_CLASS(AFlameThrower)))
{
// The flame thrower gives less ammo when given in a dialog
static_cast<AWeapon*>(item)->AmmoGive1 = 40;
}
item->flags |= MF_DROPPED;
if (!item->TryPickup (players[player].mo))
{
item->Destroy ();
Conversation_TakeStuff = false;
}
}
break;
case CONV_TAKEINVENTORY:
{
AInventory *item = players[player].ConversationPC->FindInventory (PClass::FindClass (ReadString (stream)));
if (item != NULL)
{
item->Amount -= ReadWord (stream);
if (item->Amount <= 0)
{
item->Destroy ();
}
}
}
break;
case CONV_SETNULL:
players[player].ConversationFaceTalker = NULL;
players[player].ConversationNPC = NULL;
players[player].ConversationPC = NULL;
players[player].ConversationNPCAngle = NULL;
break;
default:
break;
}
} }

View file

@ -5,8 +5,7 @@
// users can edit as simple text files. Particularly useful would be // users can edit as simple text files. Particularly useful would be
// the ability to call ACS functions to implement AppearsWhen properties // the ability to call ACS functions to implement AppearsWhen properties
// and ACS scripts to implement ActionTaken properties. // and ACS scripts to implement ActionTaken properties.
// TODO: Make this work in multiplayer and in demos. Multiplayer probably // TODO: Make this work in demos.
// isn't possible for Strife conversations, but demo playback should be.
struct FStrifeDialogueReply; struct FStrifeDialogueReply;
class FTexture; class FTexture;
@ -48,6 +47,16 @@ struct FStrifeDialogueReply
FBrokenLines *ReplyLines; FBrokenLines *ReplyLines;
}; };
// [CW] These are used to make conversations work.
enum
{
CONV_NPCANGLE,
CONV_ANIMATE,
CONV_GIVEINVENTORY,
CONV_TAKEINVENTORY,
CONV_SETNULL,
};
extern TArray<FStrifeDialogueNode *> StrifeDialogues; extern TArray<FStrifeDialogueNode *> StrifeDialogues;
// There were 344 types in Strife, and Strife conversations refer // There were 344 types in Strife, and Strife conversations refer
@ -62,4 +71,6 @@ void P_FreeStrifeConversations ();
void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveangle); void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveangle);
void P_ResumeConversation (); void P_ResumeConversation ();
void P_ConversationCommand (int player, BYTE **stream);
#endif #endif

View file

@ -281,7 +281,11 @@ player_s::player_s()
crouchdir(0), crouchdir(0),
crouchfactor(0), crouchfactor(0),
crouchoffset(0), crouchoffset(0),
crouchviewdelta(0) crouchviewdelta(0),
ConversationNPC(0),
ConversationPC(0),
ConversationNPCAngle(0),
ConversationFaceTalker(0)
{ {
memset (&cmd, 0, sizeof(cmd)); memset (&cmd, 0, sizeof(cmd));
memset (&userinfo, 0, sizeof(userinfo)); memset (&userinfo, 0, sizeof(userinfo));
@ -315,6 +319,8 @@ size_t player_s::FixPointers (const DObject *old, DObject *rep)
if (last_mate == old) last_mate = replacement, changed++; if (last_mate == old) last_mate = replacement, changed++;
if (ReadyWeapon == old) ReadyWeapon = static_cast<AWeapon *>(rep), changed++; if (ReadyWeapon == old) ReadyWeapon = static_cast<AWeapon *>(rep), changed++;
if (PendingWeapon == old) PendingWeapon = static_cast<AWeapon *>(rep), changed++; if (PendingWeapon == old) PendingWeapon = static_cast<AWeapon *>(rep), changed++;
if (ConversationNPC == old) ConversationNPC = replacement, changed++;
if (ConversationPC == old) ConversationPC = replacement, changed++;
return changed; return changed;
} }
@ -331,6 +337,8 @@ size_t player_s::PropagateMark()
GC::Mark(mate); GC::Mark(mate);
GC::Mark(last_mate); GC::Mark(last_mate);
GC::Mark(ReadyWeapon); GC::Mark(ReadyWeapon);
GC::Mark(ConversationNPC);
GC::Mark(ConversationPC);
if (PendingWeapon != WP_NOCHANGE) if (PendingWeapon != WP_NOCHANGE)
{ {
GC::Mark(PendingWeapon); GC::Mark(PendingWeapon);
@ -2419,7 +2427,11 @@ void player_s::Serialize (FArchive &arc)
<< BlendB << BlendB
<< BlendA << BlendA
<< accuracy << stamina << accuracy << stamina
<< LogText; << LogText
<< ConversationNPC
<< ConversationPC
<< ConversationNPCAngle
<< ConversationFaceTalker;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
arc << frags[i]; arc << frags[i];

View file

@ -54,7 +54,7 @@
// Version identifier for network games. // Version identifier for network games.
// Bump it every time you do a release unless you're certain you // Bump it every time you do a release unless you're certain you
// didn't change anything that will affect sync. // didn't change anything that will affect sync.
#define NETGAMEVERSION 215 #define NETGAMEVERSION 216
// Version stored in the ini's [LastRun] section. // Version stored in the ini's [LastRun] section.
// Bump it if you made some configuration change that you want to // Bump it if you made some configuration change that you want to
@ -64,7 +64,7 @@
// Protocol version used in demos. // Protocol version used in demos.
// Bump it if you change existing DEM_ commands or add new ones. // Bump it if you change existing DEM_ commands or add new ones.
// Otherwise, it should be safe to leave it alone. // Otherwise, it should be safe to leave it alone.
#define DEMOGAMEVERSION 0x20B #define DEMOGAMEVERSION 0x20C
// Minimum demo version we can play. // Minimum demo version we can play.
// Bump it whenever you change or remove existing DEM_ commands. // Bump it whenever you change or remove existing DEM_ commands.
@ -75,7 +75,7 @@
// SAVESIG should match SAVEVER. // SAVESIG should match SAVEVER.
// MINSAVEVER is the minimum level snapshot version that can be loaded. // MINSAVEVER is the minimum level snapshot version that can be loaded.
#define MINSAVEVER 849 #define MINSAVEVER 854
#if SVN_REVISION_NUMBER < MINSAVEVER #if SVN_REVISION_NUMBER < MINSAVEVER
// Never write a savegame with a version lower than what we need // Never write a savegame with a version lower than what we need