It is currently Fri May 31, 2024 6:15 am





dialogues
{
TalkWithAlyx //This is a dialog*
{
choiceset //This is a set of choices*
{
option1 //This is an option name*
{
"Text" "Hi, i'm someone" //The text to show in game**
"wave" "NPC_Antlion.TrappedMetal" //A sound to play when this option is selected ***
"output" 1 //# output (see text below)
"next" "alyxLovesU" //The next choiceset to go to (empty means: exit dialog)
}
option2
{
"Text" "Hi, i'm nobody"
"wave" "/wav"
"output" 2
"next" "alyxKnowsU"
}
}
alyxLovesU
{
option1
{
"Text" "Marry me!"
"wave" "/wav"
"next" ""
}
option2
{
"Text" "Hi, i'm nobody"
"wave" "/wav"
"output" 2
"next" "alyxKnowsU"
}
option3
{
"Text" "Bambino???"
}
}
alyxKnowsU
{
option1
{
"Text" "But, but...!"
"wave" "/wav"
"next" ""
}
option2
{
"Text" "Bye!"
"wave" "/wav"
"next" ""
}
}
}
}
*name can be anything, but CANNOT have spaces or the '#' symbol (EVER) and must be < 128 characters long. Names have no other purpose than to create unique identifiers
**Text can contain " by using escape signs ('\"'): Note: use '\\' to display '\'. Newlines/Enters are not supported
***Wave refers to soundscripts (eg npc_alyx.whine). Unfortunately source doesn't really want you to 'just' play wave-files. Use forward slashes as path separator.
#include "cbase.h"
#include "filesystem.h"
#include "DialogShared.h"
//for ansi-to-unicode
#include "vgui/ILocalize.h"
#include "tier3/tier3.h"
#include <KeyValues.h>
CDialogEntry::CDialogEntry(const char* dialogName, const char* convosetName, KeyValues* source)
{
#if !defined CLIENT_DLL
//The server needs the data for which sound to play...
const char *wave = source->GetString("wave");
if (strlen(wave)==0)
Wave = NULL;
else
{
Wave = new char[strlen(wave)+1];
strcpy(Wave, wave);
}
//What output to send...
Output = source->GetInt("output", 0);
//What the next step in the dialog will be....
const char *next = source->GetString("next");
if ( strlen(next)==0 )
Next = NULL;
else
{
if (strstr(next,"#")!= NULL)
Warning("The # character is not allowed in dialog entry names");
Next = new char[strlen(dialogName) + strlen(next) + 2];
strcpy(Next, dialogName);
strcat(Next, "#");
strcat(Next, next);
}
#else
//The client just needs to know the text it needs to draw
const char *text = source->GetString("text");
Text = new wchar_t[strlen(text)+1];
g_pVGuiLocalize->ConvertANSIToUnicode( text, Text, sizeof(wchar_t) * (1+strlen(text)));
#endif
const char *name = source->GetName();
Name = new char[ strlen(name) + strlen(convosetName) + strlen(dialogName) + 3];
strcpy(Name, dialogName);
strcat(Name, "#");
strcat(Name, convosetName);
strcat(Name, "#");
strcat(Name, name);
}
CDialogEntry::~CDialogEntry()
{
#if !defined CLIENT_DLL
delete [] Name;
delete [] Wave;
delete [] Next;
#else
delete [] Text;
#endif
};
DialogMgr::DialogMgr() : CAutoGameSystem("DialogManager")
{
g_pDialogMgr = this;
ActiveDialog = NULL;
}
void DialogMgr::LevelInitPreEntityAllSystems( char const* pMapName )
{
ActiveDialog = NULL;
}
bool DialogMgr::Init()
{
//Load the dialog entries from file
KeyValues* kv = new KeyValues("");
kv->UsesEscapeSequences(true);
kv->LoadFromFile(filesystem, "resource/dialogentries.txt");
if ( !kv )
{
Warning( "Unable to load dialogentries.txt\n" );
return true;
}
//loop all subkeys/dialogs
for(KeyValues *convo = kv->GetFirstSubKey(); convo; convo = convo->GetNextKey() )
{
//loop all subkeys/choicesets
for ( KeyValues *choiceset = convo->GetFirstSubKey(); choiceset; choiceset = choiceset->GetNextKey() )
{
//loop entries in these sets
for( KeyValues * convoentry = choiceset->GetFirstSubKey(); convoentry; convoentry = convoentry->GetNextKey() )
{
dialogEntries.AddToTail( new CDialogEntry(convo->GetName(), choiceset->GetName(), convoentry) );
}
}
}
kv->deleteThis();
return true;
}
//GetDialogEntryByName: returns an entry by name or NULL of not found (or none present)
//dont call me with NULL...
CDialogEntry* DialogMgr::GetDialogEntriesByName(const char* name, CDialogEntry* previous)
{
//if there are no entries present, (re)try to load them from the file
if (dialogEntries.Count()==0)
return NULL;
//Iterate all items, starting at 'previous'
int lengthOfName = strlen(name);
for(int x=(previous == NULL ? 0 : dialogEntries.Find(previous)+1); x<dialogEntries.Count();x++)
{
if (strncmp(name, dialogEntries[x]->Name, lengthOfName) == 0)
{
return dialogEntries[x];
}
}
//No entries found with this name!
return NULL;
}
static DialogMgr s_DialogMgr;
DialogMgr *g_pDialogMgr = &s_DialogMgr;
#include "cbase.h"
class CDialogEntry
{
public:
char* Name;
#if !defined CLIENT_DLL
int Output;
char* Next;
char* Wave;
#else
wchar_t* Text;
#endif
CDialogEntry(const char* dialogName, const char* convosetName, KeyValues* source);
~CDialogEntry();
};
class DialogMgr : public CAutoGameSystem
{
CUtlVector<CDialogEntry*> dialogEntries;
void LoadDialogEntries();
public:
CBaseEntity* ActiveDialog;
bool Init();
void LevelInitPreEntityAllSystems( char const* pMapName );
DialogMgr();
~DialogMgr() { dialogEntries.PurgeAndDeleteElements(); }
CDialogEntry* GetDialogEntriesByName(const char* name, CDialogEntry* previous = NULL);
};
extern DialogMgr* g_pDialogMgr;
#include "cbase.h"
#include "DialogShared.h"
#include "engine/IEngineSound.h"
class CLogicDialog;
//up this if you want to have more outputs linked from your dialog tree, but mind you also need to alter the DATADESC
#define NUM_OUTPUTS 8
class CLogicDialog : public CLogicalEntity
{
DECLARE_CLASS (CLogicDialog, CLogicalEntity);
DECLARE_DATADESC();
//fired when player exits the dialog manually without selecting a convo
COutputEvent m_hOnAbort;
//A total of eight outputs you can trigger from your dialog :)
COutputEvent m_hOutputs [NUM_OUTPUTS];
//Gonna remember if i'm active, so i can restore the dialog after a save-load
bool m_bIsActive;
//dialogentry as set in hammer
char* m_strDialogEntry;
//active entry
char* m_strActiveDialogEntry;
void ShowDialog()
{
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if (!pPlayer) return;
g_pDialogMgr->ActiveDialog = this;
m_bIsActive = true;
CDialogEntry *e = g_pDialogMgr->GetDialogEntriesByName(m_strActiveDialogEntry);
if (!FStrEq(m_strActiveDialogEntry, "disabled") && !e)
{
Warning("Unknown dialog entry '%s', cannot init dialogue\n", m_strActiveDialogEntry);
HideDialog();//in case it was already open
}
else
{
CSingleUserRecipientFilter filter(pPlayer);
UserMessageBegin(filter, "InitDialogue" );
//the full name of e will be the exact entry, but we want to show the option collection instead
//the full name is ConversationName#optioncollection#optionname, so we need to write the substring 'ConversationName#optioncollection'
if (!e)
WRITE_STRING(m_strActiveDialogEntry);
else
{
char buff [257];//2x MAX_KEY+1
strcpy(buff, e->Name);
for(int x = strlen(buff)-1;x>=0;x--)
{
if (buff[x] == '#')
{
buff[x] = '\0';
break;
}
}
WRITE_STRING(buff);
}
MessageEnd();
}
}
void HideDialog()
{
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if (!pPlayer) return;
g_pDialogMgr->ActiveDialog = NULL;
m_bIsActive = true;
CSingleUserRecipientFilter filter(pPlayer);
UserMessageBegin(filter, "InitDialogue" );
WRITE_STRING("");//empty string means: NO dialog
MessageEnd();
}
public:
void OnRestore()
{
if (m_bIsActive)
{
ShowDialog();
}
}
void ShowDialog( inputdata_t &inputData)
{
if (m_strActiveDialogEntry)
{
delete [] m_strActiveDialogEntry;
}
m_strActiveDialogEntry = new char[strlen(m_strDialogEntry)+1];
strcpy(m_strActiveDialogEntry, m_strDialogEntry);
ShowDialog();
}
void Precache()
{
//Make sure we've precached all sounds we are going to use
for(CDialogEntry* pEntry = g_pDialogMgr->GetDialogEntriesByName( m_strDialogEntry ); pEntry; pEntry = g_pDialogMgr->GetDialogEntriesByName( m_strDialogEntry, pEntry ) )
{
if (pEntry->Wave)
PrecacheScriptSound(pEntry->Wave);
}
}
void Spawn()
{
BaseClass::Spawn();
Precache();
}
//Abort the dialog
void AbortDialog(inputdata_t &inputData)
{
HideDialog();
m_hOnAbort.FireOutput(this, inputData.pCaller);
}
void Think()
{
if (m_strActiveDialogEntry == NULL)
{
HideDialog();
}
else
{
ShowDialog();
}
BaseClass::Think();
}
void PlayerChose(const char* chosenOption)
{
if (!chosenOption) //user aborted :)
{
HideDialog();
m_hOnAbort.FireOutput(this, this);
}
else
{
//the chosen entry MUST be a sub-entry of the active dialog set!
CDialogEntry *pEntry = g_pDialogMgr->GetDialogEntriesByName(chosenOption);
if (strncmp(chosenOption, m_strActiveDialogEntry, strlen(m_strActiveDialogEntry)) || !pEntry)
{
//invalid choice. Player probably cheated the system!
DevMsg("Player chose invalid dialog entry");
return;
}
//is the 'output' variable set and valid? Then fire that output
if (pEntry->Output != -1 && pEntry->Output < NUM_OUTPUTS)
m_hOutputs[pEntry->Output].FireOutput(this, this);
if (m_strActiveDialogEntry)
delete [] m_strActiveDialogEntry;
//if 'next' was set, play that dialogue. Else, simply exit. But wait untill the sound stopped playing!
if (pEntry->Next != NULL)
{
m_strActiveDialogEntry = new char[strlen(pEntry->Next)+1];
strcpy(m_strActiveDialogEntry, pEntry->Next);
}
else
{
m_strActiveDialogEntry = NULL;
}
//play the sound if it was set
if (pEntry->Wave)
{
//think as soon as the wave stopped playing, to continue the dialogue
EmitSound( pEntry->Wave );
float duration = enginesound->GetSoundDuration(pEntry->Wave);
//if the sound is invalid, duration is null, so think gets called immediately. otherwise, wait xxx seconds till pEntry->Wave is done playing
SetNextThink(gpGlobals->curtime + duration);
}
else //no sound defined, so just exit/continue
{
if (m_strActiveDialogEntry == NULL)
{
HideDialog();
}
else
{
ShowDialog();
}
}
}
}
};
BEGIN_DATADESC(CLogicDialog)
DEFINE_KEYFIELD( m_strDialogEntry, FIELD_STRING, "dialogentry"),
DEFINE_FIELD( m_strActiveDialogEntry, FIELD_STRING ),
DEFINE_FIELD( m_bIsActive, FIELD_BOOLEAN ),
DEFINE_OUTPUT( m_hOutputs[0], "Output0" ),
DEFINE_OUTPUT( m_hOutputs[1], "Output1" ),
DEFINE_OUTPUT( m_hOutputs[2], "Output2" ),
DEFINE_OUTPUT( m_hOutputs[3], "Output3" ),
DEFINE_OUTPUT( m_hOutputs[4], "Output4" ),
DEFINE_OUTPUT( m_hOutputs[5], "Output5" ),
DEFINE_OUTPUT( m_hOutputs[6], "Output6" ),
DEFINE_OUTPUT( m_hOutputs[7], "Output7" ),
DEFINE_OUTPUT(m_hOnAbort, "OnAbort"),
DEFINE_INPUTFUNC( FIELD_VOID, "ShowDialog", ShowDialog),
DEFINE_INPUTFUNC( FIELD_VOID, "AbortDialog", AbortDialog),
END_DATADESC()
LINK_ENTITY_TO_CLASS(logic_dialog, CLogicDialog);
void CC_SelectDialogue(const CCommand& args)
{
if (g_pDialogMgr->ActiveDialog != NULL)
{
((CLogicDialog*)g_pDialogMgr->ActiveDialog)->PlayerChose(args.ArgC() == 2 ? args.Arg(1) : NULL);
}
}
static ConCommand SelectDialogue("select_dialogue", CC_SelectDialogue, "Select dialogue option <index>", FCVAR_CHEAT );
#include "cbase.h"
#include "hud.h"
#include "hud_macros.h"
#include "iclientmode.h"
#include <vgui/ISurface.h>
#include "hudelement.h"
#include "DialogShared.h"
#include <vgui/IInput.h>
#include <vgui_controls/Panel.h>
class CHudDialogue : public CHudElement, public vgui::Panel
{
DECLARE_CLASS_SIMPLE( CHudDialogue, vgui::Panel );
CUtlVector<CDialogEntry*> ActiveEntries;
bool ResetHeight;
bool DialogueVisible;
vgui::HFont m_hFont;
vgui::HScheme scheme;
public:
void LevelInit()
{
SetEnabled(false);
DialogueVisible = false;//I'd use SetVisible() but Valve's code has a nasty habit of not working correctly. Or at all.
SetMouseInputEnabled(false);
SetKeyBoardInputEnabled(false);
}
void Paint()
{
if (!DialogueVisible) return;
int fntTall = vgui::surface()->GetFontTall(m_hFont) + 4;
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
if ( !pPlayer )
return;
if (ResetHeight)
{
SetTall(20 + (fntTall * ActiveEntries.Count()));
int x, y;
GetPos(x,y);
SetPos(x, GetParent()->GetTall() - GetTall());
ResetHeight = false;
}
vgui::surface()->DrawSetColor(0,0,0,100);
vgui::surface()->DrawFilledRect(0,0, GetWide(), GetTall());
vgui::surface()->DrawSetTextColor(100,100,100,150);
vgui::surface()->DrawSetTextFont(m_hFont);
//get cursor position
int cx, cy;
vgui::input()->GetCursorPosition(cx,cy);
ScreenToLocal(cx,cy);
for(int x=0;x<ActiveEntries.Count();x++)
{
if (IsEnabled() && cy > 10 + (x*fntTall) && cy < 10 + fntTall + (x*fntTall))
{
//Draw a highlighting box, but only when the cursor is hovering over me and i'm not disabled
vgui::surface()->DrawSetColor(0,0,50,120);
vgui::surface()->DrawFilledRect(15, 10 + (x*fntTall), GetWide()-15, 10 + (x*fntTall)+fntTall);
}
//draw a number....
vgui::surface()->DrawSetTextPos(15, 12+ (x*fntTall));
vgui::surface()->DrawUnicodeChar('1'+x);
//draw the text...
vgui::surface()->DrawSetTextPos(30, 12 + (x*fntTall));
vgui::surface()->DrawUnicodeString(ActiveEntries[x]->Text);
}
}
void MsgFunc_InitDialogue(bf_read &msg)
{
ResetHeight = true;//force resize of this control
char dialogentry[257];//2x MAX_KEY+1
msg.ReadString(dialogentry, 257);
if (FStrEq(dialogentry, "disabled"))//disabled mode; waits for sounds to finish playing :)
{
SetEnabled(false);
return;
}
SetEnabled(true);
ActiveEntries.RemoveAll();
if (strlen(dialogentry)>0)
{
for(CDialogEntry *e = g_pDialogMgr->GetDialogEntriesByName(dialogentry); e; e = g_pDialogMgr->GetDialogEntriesByName(dialogentry, e))
{
ActiveEntries.AddToTail(e);
}
DialogueVisible = true;
SetMouseInputEnabled(true);
SetKeyBoardInputEnabled(true);
}
if (ActiveEntries.Count()==0)
{
DialogueVisible =false;
SetKeyBoardInputEnabled(false);
SetMouseInputEnabled(false);
}
}
void OnMousePressed(vgui::MouseCode code)
{
if (code == MOUSE_LEFT && IsEnabled())
{
int cx, cy;
vgui::input()->GetCursorPosition(cx,cy);
ScreenToLocal(cx,cy);
int fntTall = vgui::surface()->GetFontTall(m_hFont) + 4;
int index = (cy - 10) / fntTall;
if (index >=0 && index < ActiveEntries.Count())
SelectDialogue(index);
}
}
void Init( void );
void ApplySchemeSettings( vgui::IScheme *pScheme )
{
m_hFont = pScheme->GetFont( "HudHintTextLarge", true );
int fntTall = vgui::surface()->GetFontTall(m_hFont);
SetTall(20 + (fntTall * ActiveEntries.Count()));
int x, y;
GetPos(x,y);
SetPos(x, GetParent()->GetTall() - GetTall());
ResetHeight = false;
BaseClass::ApplySchemeSettings( pScheme );
}
void SelectDialogue(int id)
{
char cmd[128];
strcpy(cmd, "select_dialogue ");
if (id>=0)
strcat(cmd, ActiveEntries[id]->Name);
engine->ServerCmd(cmd);
//server decides if we continue the dialog. Not me
SetEnabled(false);
}
void CHudDialogue::OnKeyCodeReleased(vgui::KeyCode code)
{
if (IsEnabled())
{
if (code > KEY_0 && code <= KEY_9)
SelectDialogue(code - KEY_1);//select 1-9
else if (code == KEY_SPACE)//key_escape doesnt work here, its reserved for the menu/console
SelectDialogue(-1);//cancel
}
}
CHudDialogue(const char *pElementName) : CHudElement(pElementName), BaseClass(NULL, pElementName)
{
SetParent( g_pClientMode->GetViewport() );//attacht it to the main screen
scheme = vgui::scheme()->LoadSchemeFromFile("resource/ClientScheme.res", "ClientScheme");//load scheme
SetPaintBackgroundEnabled( false );//we draw in paint
SetHiddenBits( HIDEHUD_PLAYERDEAD );//dont show if we're dead.
MakePopup(true, true);//make it a popup, yet do not draw it. Just hoover invisible over the screen till needed
SetMouseInputEnabled(false);//dont swallow mouse input
SetKeyBoardInputEnabled(false);//dont swallow keyboard input
}
};
//I AM HUD.
DECLARE_HUDELEMENT ( CHudDialogue );
//I listen to InitDialog usermessage
DECLARE_HUD_MESSAGE( CHudDialogue, InitDialogue )
void CHudDialogue::Init()
{
//I'm ready to recieve InitDialog messages now.
HOOK_HUD_MESSAGE( CHudDialogue, InitDialogue );
}
CHudDialogue
{
"fieldname" "CHudDialogue"
"visible" "0"
"wide" "500"
"tall" "100"
"xpos" "c-250"
"ypos" "380"
}
usermessages->Register( "InitDialogue", -1);
@PointClass base(Targetname) = logic_dialog : "Dialog entity"
[
dialogentry(string) : "Dialog Entry" : "" : "The dialog to start in dialogentries.txt."
output Output1(void) : "Fired when this Output is triggered in the dialogue."
output Output2(void) : "Fired when this Output is triggered in the dialogue."
output Output3(void) : "Fired when this Output is triggered in the dialogue."
output Output4(void) : "Fired when this Output is triggered in the dialogue."
output Output5(void) : "Fired when this Output is triggered in the dialogue."
output Output6(void) : "Fired when this Output is triggered in the dialogue."
output Output7(void) : "Fired when this Output is triggered in the dialogue."
output Output8(void) : "Fired when this Output is triggered in the dialogue."
output Output9(void) : "Fired when this Output is triggered in the dialogue."
output OnAbort(void) : "Fired when the dialogue was cancelled by user or input."
input ShowDialog(void) : "Show the dialog (cancels any active dialog)"
input AbortDialog(void) : "Abort the dialog (has no effect if no dialog is active, but will call OnAbort)."
]











2>logic_dialog.obj : error LNK2001: unresolved external symbol "class DialogMgr * g_pDialogMgr" (?g_pDialogMgr@@3PAVDialogMgr@@A)
2>logic_dialog.obj : error LNK2019: unresolved external symbol "public: class CDialogEntry * __thiscall DialogMgr::GetDialogEntriesByName(char const *,class CDialogEntry *)" (?GetDialogEntriesByName@DialogMgr@@QAEPAVCDialogEntry@@PBDPAV2@@Z) referenced in function "private: void __thiscall CLogicDialog::ShowDialog(void)" (?ShowDialog@CLogicDialog@@AAEXXZ)
2>.\Release_episodic\Server.dll : fatal error LNK1120: 2 unresolved externals
========== Build: 1 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========




Users browsing this forum: No registered users