mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-15 08:41:59 +00:00
- merge USDF branch into trunk.
- add USDF spexs. SVN r2561 (trunk)
This commit is contained in:
parent
6f82db47b8
commit
b452bec0ee
23 changed files with 1227 additions and 274 deletions
158
specs/usdf.txt
Normal file
158
specs/usdf.txt
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
===============================================================================
|
||||||
|
Universal Strife Dialog Format Specification v2.0 - 08/20/10
|
||||||
|
|
||||||
|
Written by Braden "Blzut3" Obrzut - admin@maniacsvault.net
|
||||||
|
|
||||||
|
Defined with input from:
|
||||||
|
|
||||||
|
CodeImp
|
||||||
|
Gez
|
||||||
|
Graf Zahl
|
||||||
|
Quasar
|
||||||
|
et al.
|
||||||
|
|
||||||
|
Copyright (c) 2010 Braden Obrzut.
|
||||||
|
Permission is granted to copy, distribute and/or modify this document
|
||||||
|
under the terms of the GNU Free Documentation License, Version 1.2
|
||||||
|
or any later version published by the Free Software Foundation;
|
||||||
|
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
I. Grammar / Syntax
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
The grammar and syntax is similar to that of UDMF. A compliant UDMF parser
|
||||||
|
should be applyable to the USDF. However, it will need to be capable of
|
||||||
|
handling sub-blocks. Unknown sub-blocks should be skipped.
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
II. Implementation Semantics
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
------------------------------------
|
||||||
|
II.A : Storage and Retrieval of Data
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
This is the same as in UDMF.
|
||||||
|
|
||||||
|
-----------------------------------
|
||||||
|
II.B : Storage Within Archive Files
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
There are two options for the USDF lump placement. This can either be a part
|
||||||
|
of the UDMF lump list or standalone. If used stand alone the lump name
|
||||||
|
DIALOGXY is used corresponding with MAPXY. For UDMF the lump shall be called
|
||||||
|
"DIALOGUE".
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
II.C : Implementation Dependence
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
USDF also implements the namespace statement. This has all the same
|
||||||
|
requirements as UDMF.
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
III. Standardized Fields
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
The following are required for all USDF complient implementations. Like UDMF,
|
||||||
|
any unknown field/function should be ignored and not treated as an error.
|
||||||
|
|
||||||
|
NOTE: "mobj" refers to Strife's conversationIDs and not doom editor numbers or
|
||||||
|
Hexen's spawnids. A valid mobj value is any positive integer greater
|
||||||
|
than or equal to 1.
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
III.A : Conversations
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Conversations are groups of pages that can be assigned to a particular object.
|
||||||
|
Implementors should preserve the IDs to allow for dynamic reassignment through
|
||||||
|
scripting although this is not a requirement.
|
||||||
|
|
||||||
|
conversation // Starts a dialog.
|
||||||
|
{
|
||||||
|
actor = <integer>; // mobj for this conversation's actor. If previously
|
||||||
|
// used, this will override the previous conversation.
|
||||||
|
|
||||||
|
page // Starts a new page. Pages are automatically numbered starting at 0.
|
||||||
|
{
|
||||||
|
name = <string>; // Name that goes in the upper left hand corner
|
||||||
|
panel = <string>; // Name of lump to render as the background.
|
||||||
|
voice = <string>; // Narration sound lump.
|
||||||
|
dialog = <string>; // Dialog of the page.
|
||||||
|
drop = <integer>; // mobj for the object to drop if the actor is
|
||||||
|
// killed.
|
||||||
|
link = <integer>; // Page to jump to if all ifitem conditions are
|
||||||
|
// satisified.
|
||||||
|
|
||||||
|
// jumps to the specified page if the player has the specified amount
|
||||||
|
// or more of item in their inventory. This can be repeated as many
|
||||||
|
// times as the author wants, all conditions must be met for the
|
||||||
|
// jump to occur.
|
||||||
|
ifitem
|
||||||
|
{
|
||||||
|
item = <integer>; // mobj of item to check.
|
||||||
|
amount = <integer>; // amount required to be in inventory.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choices shall be automatically numbered.
|
||||||
|
choice
|
||||||
|
{
|
||||||
|
text = <string>; // Name of the choice.
|
||||||
|
|
||||||
|
// The amount of an item needed to successfully pick this option.
|
||||||
|
// This can be repeated, but only the first will be shown (provided
|
||||||
|
// diaplaycost is true). All costs must be satisfied for success.
|
||||||
|
cost
|
||||||
|
{
|
||||||
|
item = <integer>; // Item that is required for this option.
|
||||||
|
amount = <integer>; // Minimum amount of the item needed.
|
||||||
|
}
|
||||||
|
|
||||||
|
displaycost = <bool>; // Weather the cost should be
|
||||||
|
// displayed with the option.
|
||||||
|
// If no cost is specified this should
|
||||||
|
// be ignored.
|
||||||
|
yesmessage = <string>; // Text to add to console when choice
|
||||||
|
// is accepted.
|
||||||
|
nomessage = <string>; // Text to add to console when choice
|
||||||
|
// is denied.
|
||||||
|
|
||||||
|
log = <string>; // LOG entry to use on success.
|
||||||
|
giveitem = <integer>; // Gives the specified item upon
|
||||||
|
// success.
|
||||||
|
// The following are the same as the special for linedefs in UDMF.
|
||||||
|
// They are executed on success.
|
||||||
|
special = <integer>;
|
||||||
|
arg0 = <integer>;
|
||||||
|
arg1 = <integer>;
|
||||||
|
arg2 = <integer>;
|
||||||
|
arg3 = <integer>;
|
||||||
|
arg4 = <integer>;
|
||||||
|
|
||||||
|
nextpage = <integer>; // Sets the next page.
|
||||||
|
closedialog = <bool>; // Should the dialog be closed upon
|
||||||
|
// selecting this choice?
|
||||||
|
// Default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-------------------------------
|
||||||
|
III.B : Including Other Dialogs
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
Unlike the original Strife dialog format. The lump "SCRIPT00" should not be
|
||||||
|
included automatically. Instead the user must specify this behavior by using
|
||||||
|
the include function, which takes the name of a lump to include. Include only
|
||||||
|
needs to be available in the global scope and for compatibility reasons, must
|
||||||
|
include the result of the script and not act like a preprocessor statement.
|
||||||
|
|
||||||
|
include = <string>;
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
EOF
|
||||||
|
===============================================================================
|
56
specs/usdf_zdoom.txt
Normal file
56
specs/usdf_zdoom.txt
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
===============================================================================
|
||||||
|
Universal Strife Dialog Format ZDoom extensions v1.0 - 14.08.2010
|
||||||
|
|
||||||
|
Copyright (c) 2010 Christoph Oelckers.
|
||||||
|
Permission is granted to copy, distribute and/or modify this document
|
||||||
|
under the terms of the GNU Free Documentation License, Version 1.2
|
||||||
|
or any later version published by the Free Software Foundation;
|
||||||
|
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
I. Grammar / Syntax
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
No changes.
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
II. Implementation Semantics
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
No changes.
|
||||||
|
|
||||||
|
=======================================
|
||||||
|
III. Standardized Fields
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
ZDoom implements the base specification as described with one important change:
|
||||||
|
To take advantage of named actor classes any field specifying an actor type
|
||||||
|
by a conversationID takes a class name instead.
|
||||||
|
This means that ZDoom dialogues are not forward-compatible with the 'Strife'
|
||||||
|
namespace. Other ports should be aware of this.
|
||||||
|
ZDoom-format dialogues need to start with the line:
|
||||||
|
|
||||||
|
namespace = "ZDoom";
|
||||||
|
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
III.A : Conversations
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
This block only lists the newly added fields. Currently ZDoom only adds one
|
||||||
|
field to the specification:
|
||||||
|
|
||||||
|
conversation // Starts a dialog.
|
||||||
|
{
|
||||||
|
id = <int>; // assigns an ID to a dialogue. IDs are used to dynamically assign
|
||||||
|
// dialogues to actors. For 'Strife' namespace or binary dialogues
|
||||||
|
// the standard conversation ID ('actor' property) is used instead
|
||||||
|
// for this purpose but since 'ZDoom' namespace requires the actor
|
||||||
|
// to be a class name it needs a separate field for this.
|
||||||
|
}
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
EOF
|
||||||
|
===============================================================================
|
|
@ -703,6 +703,7 @@ add_executable( zdoom WIN32
|
||||||
p_tick.cpp
|
p_tick.cpp
|
||||||
p_trace.cpp
|
p_trace.cpp
|
||||||
p_udmf.cpp
|
p_udmf.cpp
|
||||||
|
p_usdf.cpp
|
||||||
p_user.cpp
|
p_user.cpp
|
||||||
p_writemap.cpp
|
p_writemap.cpp
|
||||||
p_xlat.cpp
|
p_xlat.cpp
|
||||||
|
|
|
@ -77,7 +77,7 @@ DEFINE_SPECIAL(Teleport_EndGame, 75, 0, 0, 0)
|
||||||
DEFINE_SPECIAL(TeleportOther, 76, 3, 3, 3)
|
DEFINE_SPECIAL(TeleportOther, 76, 3, 3, 3)
|
||||||
DEFINE_SPECIAL(TeleportGroup, 77, 5, 5, 5)
|
DEFINE_SPECIAL(TeleportGroup, 77, 5, 5, 5)
|
||||||
DEFINE_SPECIAL(TeleportInSector, 78, 4, 5, 5)
|
DEFINE_SPECIAL(TeleportInSector, 78, 4, 5, 5)
|
||||||
|
DEFINE_SPECIAL(Thing_SetConversation, 79, 2, 2, 2)
|
||||||
DEFINE_SPECIAL(ACS_Execute, 80, 1, 5, 5)
|
DEFINE_SPECIAL(ACS_Execute, 80, 1, 5, 5)
|
||||||
DEFINE_SPECIAL(ACS_Suspend, 81, 2, 2, 2)
|
DEFINE_SPECIAL(ACS_Suspend, 81, 2, 2, 2)
|
||||||
DEFINE_SPECIAL(ACS_Terminate, 82, 2, 2, 2)
|
DEFINE_SPECIAL(ACS_Terminate, 82, 2, 2, 2)
|
||||||
|
|
|
@ -892,8 +892,9 @@ public:
|
||||||
FState *MeleeState;
|
FState *MeleeState;
|
||||||
FState *MissileState;
|
FState *MissileState;
|
||||||
|
|
||||||
// [RH] The dialogue to show when this actor is "used."
|
|
||||||
FStrifeDialogueNode *Conversation;
|
int ConversationRoot; // THe root of the current dialogue
|
||||||
|
FStrifeDialogueNode *Conversation; // [RH] The dialogue to show when this actor is "used."
|
||||||
|
|
||||||
// [RH] Decal(s) this weapon/projectile generates on impact.
|
// [RH] Decal(s) this weapon/projectile generates on impact.
|
||||||
FDecalBase *DecalGenerator;
|
FDecalBase *DecalGenerator;
|
||||||
|
|
|
@ -90,6 +90,23 @@ char *copystring (const char *s)
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// ncopystring
|
||||||
|
//
|
||||||
|
// If the string has no content, returns NULL. Otherwise, returns a copy.
|
||||||
|
//
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
char *ncopystring (const char *string)
|
||||||
|
{
|
||||||
|
if (string == NULL || string[0] == 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return copystring (string);
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// ReplaceString
|
// ReplaceString
|
||||||
|
|
|
@ -36,6 +36,7 @@ int ParseNum (const char *str);
|
||||||
bool IsNum (const char *str); // [RH] added
|
bool IsNum (const char *str); // [RH] added
|
||||||
|
|
||||||
char *copystring(const char *s);
|
char *copystring(const char *s);
|
||||||
|
char *ncopystring(const char *s);
|
||||||
void ReplaceString (char **ptr, const char *str);
|
void ReplaceString (char **ptr, const char *str);
|
||||||
|
|
||||||
bool CheckWildcards (const char *pattern, const char *text);
|
bool CheckWildcards (const char *pattern, const char *text);
|
||||||
|
|
|
@ -317,6 +317,7 @@ PClass *PClass::CreateDerivedClass (FName name, unsigned int size)
|
||||||
info->DamageFactors = NULL;
|
info->DamageFactors = NULL;
|
||||||
info->PainChances = NULL;
|
info->PainChances = NULL;
|
||||||
info->ColorSets = NULL;
|
info->ColorSets = NULL;
|
||||||
|
info->ConversationID = -1;
|
||||||
m_RuntimeActors.Push (type);
|
m_RuntimeActors.Push (type);
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
|
@ -411,6 +412,7 @@ void PClass::InitializeActorInfo ()
|
||||||
info->DamageFactors = NULL;
|
info->DamageFactors = NULL;
|
||||||
info->PainChances = NULL;
|
info->PainChances = NULL;
|
||||||
info->ColorSets = NULL;
|
info->ColorSets = NULL;
|
||||||
|
info->ConversationID = -1;
|
||||||
m_RuntimeActors.Push (this);
|
m_RuntimeActors.Push (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -290,6 +290,10 @@ template<> inline FArchive &operator<< <FFont> (FArchive &arc, FFont* &font)
|
||||||
return SerializeFFontPtr (arc, font);
|
return SerializeFFontPtr (arc, font);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FStrifeDialogueNode;
|
||||||
|
template<> FArchive &operator<< (FArchive &arc, FStrifeDialogueNode *&node);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<class T,class TT>
|
template<class T,class TT>
|
||||||
inline FArchive &operator<< (FArchive &arc, TArray<T,TT> &self)
|
inline FArchive &operator<< (FArchive &arc, TArray<T,TT> &self)
|
||||||
|
|
|
@ -68,7 +68,7 @@ void A_Unblock(AActor *self, bool drop)
|
||||||
|
|
||||||
self->flags &= ~MF_SOLID;
|
self->flags &= ~MF_SOLID;
|
||||||
|
|
||||||
// If the self has a conversation that sets an item to drop, drop that.
|
// If the actor has a conversation that sets an item to drop, drop that.
|
||||||
if (self->Conversation != NULL && self->Conversation->DropType != NULL)
|
if (self->Conversation != NULL && self->Conversation->DropType != NULL)
|
||||||
{
|
{
|
||||||
P_DropItem (self, self->Conversation->DropType, -1, 256);
|
P_DropItem (self, self->Conversation->DropType, -1, 256);
|
||||||
|
|
|
@ -203,6 +203,7 @@ struct FActorInfo
|
||||||
BYTE GameFilter;
|
BYTE GameFilter;
|
||||||
BYTE SpawnID;
|
BYTE SpawnID;
|
||||||
SWORD DoomEdNum;
|
SWORD DoomEdNum;
|
||||||
|
int ConversationID;
|
||||||
FStateLabels *StateList;
|
FStateLabels *StateList;
|
||||||
DmgFactors *DamageFactors;
|
DmgFactors *DamageFactors;
|
||||||
PainChanceList *PainChances;
|
PainChanceList *PainChances;
|
||||||
|
|
|
@ -443,3 +443,23 @@ xx(blockprojectiles)
|
||||||
xx(blockuse)
|
xx(blockuse)
|
||||||
|
|
||||||
xx(Renderstyle)
|
xx(Renderstyle)
|
||||||
|
|
||||||
|
// USDF keywords
|
||||||
|
xx(Amount)
|
||||||
|
xx(Text)
|
||||||
|
xx(Displaycost)
|
||||||
|
xx(Yesmessage)
|
||||||
|
xx(Nomessage)
|
||||||
|
xx(Log)
|
||||||
|
xx(Giveitem)
|
||||||
|
xx(Nextpage)
|
||||||
|
xx(Closedialog)
|
||||||
|
xx(Cost)
|
||||||
|
xx(Page)
|
||||||
|
xx(Count)
|
||||||
|
xx(Name)
|
||||||
|
xx(Panel)
|
||||||
|
xx(Dialog)
|
||||||
|
xx(Ifitem)
|
||||||
|
xx(Choice)
|
||||||
|
xx(Link)
|
||||||
|
|
|
@ -59,6 +59,8 @@
|
||||||
#include "doomstat.h"
|
#include "doomstat.h"
|
||||||
#include "c_console.h"
|
#include "c_console.h"
|
||||||
#include "sbar.h"
|
#include "sbar.h"
|
||||||
|
#include "farchive.h"
|
||||||
|
#include "p_lnspec.h"
|
||||||
|
|
||||||
// The conversations as they exist inside a SCRIPTxx lump.
|
// The conversations as they exist inside a SCRIPTxx lump.
|
||||||
struct Response
|
struct Response
|
||||||
|
@ -103,19 +105,20 @@ void GiveSpawner (player_t *player, const PClass *type);
|
||||||
|
|
||||||
TArray<FStrifeDialogueNode *> StrifeDialogues;
|
TArray<FStrifeDialogueNode *> StrifeDialogues;
|
||||||
|
|
||||||
// There were 344 types in Strife, and Strife conversations refer
|
typedef TMap<int, const PClass *> FStrifeTypeMap; // maps conversation IDs to actor classes
|
||||||
// to their index in the mobjinfo table. This table indexes all
|
typedef TMap<int, int> FDialogueIDMap; // maps dialogue IDs to dialogue array index (for ACS)
|
||||||
// the Strife actor types in the order Strife had them and is
|
typedef TMap<FName, int> FDialogueMap; // maps actor class names to dialogue array index
|
||||||
// initialized as part of the actor's setup in infodefaults.cpp.
|
|
||||||
const PClass *StrifeTypes[1001];
|
static FStrifeTypeMap StrifeTypes;
|
||||||
|
static FDialogueIDMap DialogueRoots;
|
||||||
|
static FDialogueMap ClassRoots;
|
||||||
|
|
||||||
static menu_t ConversationMenu;
|
static menu_t ConversationMenu;
|
||||||
static TArray<menuitem_t> ConversationItems;
|
static TArray<menuitem_t> ConversationItems;
|
||||||
static int ConversationPauseTic;
|
static int ConversationPauseTic;
|
||||||
static bool ShowGold;
|
static bool ShowGold;
|
||||||
|
|
||||||
static void LoadScriptFile (const char *name);
|
static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool include, int type);
|
||||||
static void LoadScriptFile(FileReader *lump, int numnodes);
|
|
||||||
static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeakerType);
|
static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeakerType);
|
||||||
static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeakerType);
|
static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeakerType);
|
||||||
static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses);
|
static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses);
|
||||||
|
@ -139,13 +142,42 @@ static FBrokenLines *DialogueLines;
|
||||||
//
|
//
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
static const PClass *GetStrifeType (int typenum)
|
void SetStrifeType(int convid, const PClass *Class)
|
||||||
{
|
{
|
||||||
if (typenum > 0 && typenum < 1001)
|
StrifeTypes[convid] = Class;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetConversation(int convid, const PClass *Class, int dlgindex)
|
||||||
|
{
|
||||||
|
if (convid != -1)
|
||||||
{
|
{
|
||||||
return StrifeTypes[typenum];
|
DialogueRoots[convid] = dlgindex;
|
||||||
}
|
}
|
||||||
return NULL;
|
if (Class != NULL)
|
||||||
|
{
|
||||||
|
ClassRoots[Class->TypeName] = dlgindex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PClass *GetStrifeType (int typenum)
|
||||||
|
{
|
||||||
|
const PClass **ptype = StrifeTypes.CheckKey(typenum);
|
||||||
|
if (ptype == NULL) return NULL;
|
||||||
|
else return *ptype;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetConversation(int conv_id)
|
||||||
|
{
|
||||||
|
int *pindex = DialogueRoots.CheckKey(conv_id);
|
||||||
|
if (pindex == NULL) return -1;
|
||||||
|
else return *pindex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetConversation(FName classname)
|
||||||
|
{
|
||||||
|
int *pindex = ClassRoots.CheckKey(classname);
|
||||||
|
if (pindex == NULL) return -1;
|
||||||
|
else return *pindex;
|
||||||
}
|
}
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
@ -158,11 +190,11 @@ static const PClass *GetStrifeType (int typenum)
|
||||||
|
|
||||||
void P_LoadStrifeConversations (MapData *map, const char *mapname)
|
void P_LoadStrifeConversations (MapData *map, const char *mapname)
|
||||||
{
|
{
|
||||||
|
P_FreeStrifeConversations ();
|
||||||
if (map->Size(ML_CONVERSATION) > 0)
|
if (map->Size(ML_CONVERSATION) > 0)
|
||||||
{
|
{
|
||||||
LoadScriptFile ("SCRIPT00");
|
|
||||||
map->Seek(ML_CONVERSATION);
|
map->Seek(ML_CONVERSATION);
|
||||||
LoadScriptFile (map->file, map->Size(ML_CONVERSATION));
|
LoadScriptFile (map->lumpnum, map->file, map->Size(ML_CONVERSATION), false, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -170,10 +202,13 @@ void P_LoadStrifeConversations (MapData *map, const char *mapname)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
char scriptname[9] = { 'S','C','R','I','P','T',mapname[3],mapname[4],0 };
|
char scriptname_b[9] = { 'S','C','R','I','P','T',mapname[3],mapname[4],0 };
|
||||||
|
char scriptname_t[9] = { 'D','I','A','L','O','G',mapname[3],mapname[4],0 };
|
||||||
|
|
||||||
LoadScriptFile ("SCRIPT00");
|
if (!LoadScriptFile(scriptname_t, false, 2))
|
||||||
LoadScriptFile (scriptname);
|
{
|
||||||
|
LoadScriptFile (scriptname_b, false, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,36 +227,13 @@ void P_FreeStrifeConversations ()
|
||||||
delete node;
|
delete node;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 344; ++i)
|
DialogueRoots.Clear();
|
||||||
{
|
ClassRoots.Clear();
|
||||||
if (StrifeTypes[i] != NULL)
|
|
||||||
{
|
|
||||||
AActor * ac = GetDefaultByType (StrifeTypes[i]);
|
|
||||||
if (ac != NULL) ac->Conversation = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CurNode = NULL;
|
CurNode = NULL;
|
||||||
PrevNode = NULL;
|
PrevNode = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
//
|
|
||||||
// ncopystring
|
|
||||||
//
|
|
||||||
// If the string has no content, returns NULL. Otherwise, returns a copy.
|
|
||||||
//
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
static char *ncopystring (const char *string)
|
|
||||||
{
|
|
||||||
if (string == NULL || string[0] == 0)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return copystring (string);
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
//
|
//
|
||||||
// LoadScriptFile
|
// LoadScriptFile
|
||||||
|
@ -230,61 +242,89 @@ static char *ncopystring (const char *string)
|
||||||
//
|
//
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
static void LoadScriptFile (const char *name)
|
bool LoadScriptFile (const char *name, bool include, int type)
|
||||||
{
|
{
|
||||||
int lumpnum = Wads.CheckNumForName (name);
|
int lumpnum = Wads.CheckNumForName (name);
|
||||||
FileReader *lump;
|
FileReader *lump;
|
||||||
|
|
||||||
if (lumpnum < 0)
|
if (lumpnum < 0)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
lump = Wads.ReopenLumpNum (lumpnum);
|
lump = Wads.ReopenLumpNum (lumpnum);
|
||||||
|
|
||||||
LoadScriptFile(lump, Wads.LumpLength(lumpnum));
|
bool res = LoadScriptFile(lumpnum, lump, Wads.LumpLength(lumpnum), include, type);
|
||||||
delete lump;
|
delete lump;
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LoadScriptFile(FileReader *lump, int numnodes)
|
static bool LoadScriptFile(int lumpnum, FileReader *lump, int numnodes, bool include, int type)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
DWORD prevSpeakerType;
|
DWORD prevSpeakerType;
|
||||||
FStrifeDialogueNode *node;
|
FStrifeDialogueNode *node;
|
||||||
|
char buffer[4];
|
||||||
|
|
||||||
if (!(gameinfo.flags & GI_SHAREWARE))
|
lump->Read(buffer, 4);
|
||||||
|
lump->Seek(0, SEEK_SET);
|
||||||
|
|
||||||
|
// The binary format is so primitive that this check is enough to detect it.
|
||||||
|
bool isbinary = (buffer[0] == 0 || buffer[1] == 0 || buffer[2] == 0 || buffer[3] == 0);
|
||||||
|
|
||||||
|
if ((type == 1 && !isbinary) || (type == 2 && isbinary))
|
||||||
{
|
{
|
||||||
// Strife scripts are always a multiple of 1516 bytes because each entry
|
DPrintf("Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum));
|
||||||
// is exactly 1516 bytes long.
|
return false;
|
||||||
if (numnodes % 1516 != 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
numnodes /= 1516;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// And the teaser version has 1488-byte entries.
|
|
||||||
if (numnodes % 1488 != 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
numnodes /= 1488;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prevSpeakerType = 0;
|
if (!isbinary)
|
||||||
|
|
||||||
for (i = 0; i < numnodes; ++i)
|
|
||||||
{
|
{
|
||||||
|
P_ParseUSDF(lumpnum, lump, numnodes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!include)
|
||||||
|
{
|
||||||
|
LoadScriptFile("SCRIPT00", true, 1);
|
||||||
|
}
|
||||||
if (!(gameinfo.flags & GI_SHAREWARE))
|
if (!(gameinfo.flags & GI_SHAREWARE))
|
||||||
{
|
{
|
||||||
node = ReadRetailNode (lump, prevSpeakerType);
|
// Strife scripts are always a multiple of 1516 bytes because each entry
|
||||||
|
// is exactly 1516 bytes long.
|
||||||
|
if (numnodes % 1516 != 0)
|
||||||
|
{
|
||||||
|
DPrintf("Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
numnodes /= 1516;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
node = ReadTeaserNode (lump, prevSpeakerType);
|
// And the teaser version has 1488-byte entries.
|
||||||
|
if (numnodes % 1488 != 0)
|
||||||
|
{
|
||||||
|
DPrintf("Incorrect data format for %s.", Wads.GetLumpFullName(lumpnum));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
numnodes /= 1488;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevSpeakerType = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < numnodes; ++i)
|
||||||
|
{
|
||||||
|
if (!(gameinfo.flags & GI_SHAREWARE))
|
||||||
|
{
|
||||||
|
node = ReadRetailNode (lump, prevSpeakerType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node = ReadTeaserNode (lump, prevSpeakerType);
|
||||||
|
}
|
||||||
|
node->ThisNodeNum = StrifeDialogues.Push(node);
|
||||||
}
|
}
|
||||||
node->ThisNodeNum = StrifeDialogues.Push(node);
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
@ -316,12 +356,14 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker
|
||||||
// actor, so newly spawned actors will use this conversation by default.
|
// actor, so newly spawned actors will use this conversation by default.
|
||||||
type = GetStrifeType (speech.SpeakerType);
|
type = GetStrifeType (speech.SpeakerType);
|
||||||
node->SpeakerType = type;
|
node->SpeakerType = type;
|
||||||
if (prevSpeakerType != speech.SpeakerType)
|
|
||||||
|
if (speech.SpeakerType >= 0 && prevSpeakerType != speech.SpeakerType)
|
||||||
{
|
{
|
||||||
if (type != NULL)
|
if (type != NULL)
|
||||||
{
|
{
|
||||||
GetDefaultByType (type)->Conversation = node;
|
ClassRoots[type->TypeName] = StrifeDialogues.Size();
|
||||||
}
|
}
|
||||||
|
DialogueRoots[speech.SpeakerType] = StrifeDialogues.Size();
|
||||||
prevSpeakerType = speech.SpeakerType;
|
prevSpeakerType = speech.SpeakerType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,9 +387,11 @@ static FStrifeDialogueNode *ReadRetailNode (FileReader *lump, DWORD &prevSpeaker
|
||||||
node->DropType = GetStrifeType (speech.DropType);
|
node->DropType = GetStrifeType (speech.DropType);
|
||||||
|
|
||||||
// Items you need to have to make the speaker use a different node.
|
// Items you need to have to make the speaker use a different node.
|
||||||
|
node->ItemCheck.Resize(3);
|
||||||
for (j = 0; j < 3; ++j)
|
for (j = 0; j < 3; ++j)
|
||||||
{
|
{
|
||||||
node->ItemCheck[j] = GetStrifeType (speech.ItemCheck[j]);
|
node->ItemCheck[j].Item = GetStrifeType (speech.ItemCheck[j]);
|
||||||
|
node->ItemCheck[j].Amount = -1;
|
||||||
}
|
}
|
||||||
node->ItemCheckNode = speech.Link;
|
node->ItemCheckNode = speech.Link;
|
||||||
node->Children = NULL;
|
node->Children = NULL;
|
||||||
|
@ -385,12 +429,14 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker
|
||||||
// actor, so newly spawned actors will use this conversation by default.
|
// actor, so newly spawned actors will use this conversation by default.
|
||||||
type = GetStrifeType (speech.SpeakerType);
|
type = GetStrifeType (speech.SpeakerType);
|
||||||
node->SpeakerType = type;
|
node->SpeakerType = type;
|
||||||
if (prevSpeakerType != speech.SpeakerType)
|
|
||||||
|
if (speech.SpeakerType >= 0 && prevSpeakerType != speech.SpeakerType)
|
||||||
{
|
{
|
||||||
if (type != NULL)
|
if (type != NULL)
|
||||||
{
|
{
|
||||||
GetDefaultByType (type)->Conversation = node;
|
ClassRoots[type->TypeName] = StrifeDialogues.Size();
|
||||||
}
|
}
|
||||||
|
DialogueRoots[speech.SpeakerType] = StrifeDialogues.Size();
|
||||||
prevSpeakerType = speech.SpeakerType;
|
prevSpeakerType = speech.SpeakerType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,9 +465,11 @@ static FStrifeDialogueNode *ReadTeaserNode (FileReader *lump, DWORD &prevSpeaker
|
||||||
node->DropType = GetStrifeType (speech.DropType);
|
node->DropType = GetStrifeType (speech.DropType);
|
||||||
|
|
||||||
// Items you need to have to make the speaker use a different node.
|
// Items you need to have to make the speaker use a different node.
|
||||||
|
node->ItemCheck.Resize(3);
|
||||||
for (j = 0; j < 3; ++j)
|
for (j = 0; j < 3; ++j)
|
||||||
{
|
{
|
||||||
node->ItemCheck[j] = NULL;
|
node->ItemCheck[j].Item = NULL;
|
||||||
|
node->ItemCheck[j].Amount = -1;
|
||||||
}
|
}
|
||||||
node->ItemCheckNode = 0;
|
node->ItemCheckNode = 0;
|
||||||
node->Children = NULL;
|
node->Children = NULL;
|
||||||
|
@ -476,15 +524,18 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses)
|
||||||
|
|
||||||
// The message to record in the log for this reply.
|
// The message to record in the log for this reply.
|
||||||
reply->LogNumber = rsp->Log;
|
reply->LogNumber = rsp->Log;
|
||||||
|
reply->LogString = NULL;
|
||||||
|
|
||||||
// The item to receive when this reply is used.
|
// The item to receive when this reply is used.
|
||||||
reply->GiveType = GetStrifeType (rsp->GiveType);
|
reply->GiveType = GetStrifeType (rsp->GiveType);
|
||||||
|
reply->ActionSpecial = 0;
|
||||||
|
|
||||||
// Do you need anything special for this reply to succeed?
|
// Do you need anything special for this reply to succeed?
|
||||||
|
reply->ItemCheck.Resize(3);
|
||||||
for (k = 0; k < 3; ++k)
|
for (k = 0; k < 3; ++k)
|
||||||
{
|
{
|
||||||
reply->ItemCheck[k] = GetStrifeType (rsp->Item[k]);
|
reply->ItemCheck[k].Item = GetStrifeType (rsp->Item[k]);
|
||||||
reply->ItemCheckAmount[k] = rsp->Count[k];
|
reply->ItemCheck[k].Amount = rsp->Count[k];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplyLines is calculated when the menu is shown. It is just Reply
|
// ReplyLines is calculated when the menu is shown. It is just Reply
|
||||||
|
@ -517,7 +568,7 @@ static void ParseReplies (FStrifeDialogueReply **replyptr, Response *responses)
|
||||||
{
|
{
|
||||||
reply->QuickYes = ncopystring (rsp->Yes);
|
reply->QuickYes = ncopystring (rsp->Yes);
|
||||||
}
|
}
|
||||||
if (reply->ItemCheck[0] != 0)
|
if (reply->ItemCheck[0].Item != 0)
|
||||||
{
|
{
|
||||||
reply->QuickNo = ncopystring (rsp->No);
|
reply->QuickNo = ncopystring (rsp->No);
|
||||||
}
|
}
|
||||||
|
@ -713,13 +764,20 @@ 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.Size() > 0 && CurNode->ItemCheck[0].Item != NULL)
|
||||||
{
|
{
|
||||||
if (CheckStrifeItem (pc->player, CurNode->ItemCheck[0]) &&
|
bool jump = true;
|
||||||
CheckStrifeItem (pc->player, CurNode->ItemCheck[1]) &&
|
for (i = 0; i < (int)CurNode->ItemCheck.Size(); ++i)
|
||||||
CheckStrifeItem (pc->player, CurNode->ItemCheck[2]))
|
|
||||||
{
|
{
|
||||||
int root = FindNode (pc->player->ConversationNPC->GetDefault()->Conversation);
|
if(!CheckStrifeItem (pc->player, CurNode->ItemCheck[i].Item, CurNode->ItemCheck[i].Amount))
|
||||||
|
{
|
||||||
|
jump = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (jump)
|
||||||
|
{
|
||||||
|
int root = pc->player->ConversationNPC->ConversationRoot;
|
||||||
CurNode = StrifeDialogues[root + CurNode->ItemCheckNode - 1];
|
CurNode = StrifeDialogues[root + CurNode->ItemCheckNode - 1];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1063,9 +1121,9 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply
|
||||||
npc = player->ConversationNPC;
|
npc = player->ConversationNPC;
|
||||||
|
|
||||||
// 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 < (int)reply->ItemCheck.Size(); ++i)
|
||||||
{
|
{
|
||||||
if (!CheckStrifeItem(player, reply->ItemCheck[i], reply->ItemCheckAmount[i]))
|
if (!CheckStrifeItem(player, reply->ItemCheck[i].Item, reply->ItemCheck[i].Amount))
|
||||||
{
|
{
|
||||||
// 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 && isconsole)
|
if (reply->QuickNo && isconsole)
|
||||||
|
@ -1132,12 +1190,18 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reply->ActionSpecial != 0)
|
||||||
|
{
|
||||||
|
takestuff |= !!LineSpecials[reply->ActionSpecial](NULL, player->mo, false,
|
||||||
|
reply->Args[0], reply->Args[1], reply->Args[2], reply->Args[3], reply->Args[4]);
|
||||||
|
}
|
||||||
|
|
||||||
// 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 (takestuff)
|
||||||
{
|
{
|
||||||
for (i = 0; i < 3; ++i)
|
for (i = 0; i < (int)reply->ItemCheck.Size(); ++i)
|
||||||
{
|
{
|
||||||
TakeStrifeItem (player, reply->ItemCheck[i], reply->ItemCheckAmount[i]);
|
TakeStrifeItem (player, reply->ItemCheck[i].Item, reply->ItemCheck[i].Amount);
|
||||||
}
|
}
|
||||||
replyText = reply->QuickYes;
|
replyText = reply->QuickYes;
|
||||||
}
|
}
|
||||||
|
@ -1147,7 +1211,11 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the quest log, if needed.
|
// Update the quest log, if needed.
|
||||||
if (reply->LogNumber != 0)
|
if (reply->LogString != NULL)
|
||||||
|
{
|
||||||
|
player->SetLogText(reply->LogString);
|
||||||
|
}
|
||||||
|
else if (reply->LogNumber != 0)
|
||||||
{
|
{
|
||||||
player->SetLogNumber(reply->LogNumber);
|
player->SetLogNumber(reply->LogNumber);
|
||||||
}
|
}
|
||||||
|
@ -1162,7 +1230,7 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply
|
||||||
// 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 (npc->GetDefault()->Conversation);
|
int rootnode = npc->ConversationRoot;
|
||||||
if (reply->NextNode < 0)
|
if (reply->NextNode < 0)
|
||||||
{
|
{
|
||||||
npc->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1];
|
npc->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1];
|
||||||
|
@ -1316,3 +1384,26 @@ static void TerminalResponse (const char *str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<> FArchive &operator<< (FArchive &arc, FStrifeDialogueNode *&node)
|
||||||
|
{
|
||||||
|
DWORD convnum;
|
||||||
|
if (arc.IsStoring())
|
||||||
|
{
|
||||||
|
arc.WriteCount (node == NULL? ~0u : node->ThisNodeNum);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
convnum = arc.ReadCount();
|
||||||
|
if (convnum >= StrifeDialogues.Size())
|
||||||
|
{
|
||||||
|
node = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node = StrifeDialogues[convnum];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arc;
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
#ifndef P_CONVERSATION_H
|
#ifndef P_CONVERSATION_H
|
||||||
#define P_CONVERSATION_H 1
|
#define P_CONVERSATION_H 1
|
||||||
|
|
||||||
// TODO: Generalize the conversation system to something NWN-like that
|
#include <tarray.h>
|
||||||
// users can edit as simple text files. Particularly useful would be
|
|
||||||
// the ability to call ACS functions to implement AppearsWhen properties
|
|
||||||
// and ACS scripts to implement ActionTaken properties.
|
|
||||||
// TODO: Make this work in demos.
|
|
||||||
|
|
||||||
struct FStrifeDialogueReply;
|
struct FStrifeDialogueReply;
|
||||||
class FTexture;
|
class FTexture;
|
||||||
struct FBrokenLines;
|
struct FBrokenLines;
|
||||||
|
|
||||||
|
struct FStrifeDialogueItemCheck
|
||||||
|
{
|
||||||
|
const PClass *Item;
|
||||||
|
int Amount;
|
||||||
|
};
|
||||||
|
|
||||||
// FStrifeDialogueNode holds text an NPC says to the player
|
// FStrifeDialogueNode holds text an NPC says to the player
|
||||||
struct FStrifeDialogueNode
|
struct FStrifeDialogueNode
|
||||||
{
|
{
|
||||||
~FStrifeDialogueNode ();
|
~FStrifeDialogueNode ();
|
||||||
const PClass *DropType;
|
const PClass *DropType;
|
||||||
const PClass *ItemCheck[3];
|
TArray<FStrifeDialogueItemCheck> ItemCheck;
|
||||||
int ThisNodeNum; // location of this node in StrifeDialogues
|
int ThisNodeNum; // location of this node in StrifeDialogues
|
||||||
int ItemCheckNode; // index into StrifeDialogues
|
int ItemCheckNode; // index into StrifeDialogues
|
||||||
|
|
||||||
|
@ -36,12 +38,14 @@ struct FStrifeDialogueReply
|
||||||
|
|
||||||
FStrifeDialogueReply *Next;
|
FStrifeDialogueReply *Next;
|
||||||
const PClass *GiveType;
|
const PClass *GiveType;
|
||||||
const PClass *ItemCheck[3];
|
int ActionSpecial;
|
||||||
int ItemCheckAmount[3];
|
int Args[5];
|
||||||
|
TArray<FStrifeDialogueItemCheck> ItemCheck;
|
||||||
char *Reply;
|
char *Reply;
|
||||||
char *QuickYes;
|
char *QuickYes;
|
||||||
int NextNode; // index into StrifeDialogues
|
int NextNode; // index into StrifeDialogues
|
||||||
int LogNumber;
|
int LogNumber;
|
||||||
|
char *LogString;
|
||||||
char *QuickNo;
|
char *QuickNo;
|
||||||
bool NeedsGold;
|
bool NeedsGold;
|
||||||
|
|
||||||
|
@ -50,14 +54,16 @@ struct FStrifeDialogueReply
|
||||||
|
|
||||||
extern TArray<FStrifeDialogueNode *> StrifeDialogues;
|
extern TArray<FStrifeDialogueNode *> StrifeDialogues;
|
||||||
|
|
||||||
// There were 344 types in Strife, and Strife conversations refer
|
|
||||||
// to their index in the mobjinfo table. This table indexes all
|
|
||||||
// the Strife actor types in the order Strife had them and is
|
|
||||||
// initialized as part of the actor's setup in infodefaults.cpp.
|
|
||||||
extern const PClass *StrifeTypes[1001];
|
|
||||||
|
|
||||||
struct MapData;
|
struct MapData;
|
||||||
|
|
||||||
|
void SetStrifeType(int convid, const PClass *Class);
|
||||||
|
void SetConversation(int convid, const PClass *Class, int dlgindex);
|
||||||
|
const PClass *GetStrifeType (int typenum);
|
||||||
|
int GetConversation(int conv_id);
|
||||||
|
int GetConversation(FName classname);
|
||||||
|
|
||||||
|
bool LoadScriptFile (const char *name, bool include, int type = 0);
|
||||||
|
|
||||||
void P_LoadStrifeConversations (MapData *map, const char *mapname);
|
void P_LoadStrifeConversations (MapData *map, const char *mapname);
|
||||||
void P_FreeStrifeConversations ();
|
void P_FreeStrifeConversations ();
|
||||||
|
|
||||||
|
@ -66,4 +72,8 @@ void P_ResumeConversation ();
|
||||||
|
|
||||||
void P_ConversationCommand (int netcode, int player, BYTE **stream);
|
void P_ConversationCommand (int netcode, int player, BYTE **stream);
|
||||||
|
|
||||||
|
class FileReader;
|
||||||
|
bool P_ParseUSDF(int lumpnum, FileReader *lump, int lumplen);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3006,6 +3006,36 @@ FUNC(LS_StartConversation)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FUNC(LS_Thing_SetConversation)
|
||||||
|
// Thing_SetConversation (tid, dlg_id)
|
||||||
|
{
|
||||||
|
int dlg_index = -1;
|
||||||
|
FStrifeDialogueNode *node = NULL;
|
||||||
|
|
||||||
|
if (arg1 != 0)
|
||||||
|
{
|
||||||
|
dlg_index = GetConversation(arg1);
|
||||||
|
if (dlg_index == -1) return false;
|
||||||
|
node = StrifeDialogues[dlg_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg0 != 0)
|
||||||
|
{
|
||||||
|
FActorIterator iterator (arg0);
|
||||||
|
while ((it = iterator.Next()) != NULL)
|
||||||
|
{
|
||||||
|
it->ConversationRoot = dlg_index;
|
||||||
|
it->Conversation = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (it)
|
||||||
|
{
|
||||||
|
it->ConversationRoot = dlg_index;
|
||||||
|
it->Conversation = node;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
lnSpecFunc LineSpecials[256] =
|
lnSpecFunc LineSpecials[256] =
|
||||||
{
|
{
|
||||||
|
@ -3088,7 +3118,7 @@ lnSpecFunc LineSpecials[256] =
|
||||||
/* 76 */ LS_TeleportOther,
|
/* 76 */ LS_TeleportOther,
|
||||||
/* 77 */ LS_TeleportGroup,
|
/* 77 */ LS_TeleportGroup,
|
||||||
/* 78 */ LS_TeleportInSector,
|
/* 78 */ LS_TeleportInSector,
|
||||||
/* 79 */ LS_NOP,
|
/* 79 */ LS_Thing_SetConversation,
|
||||||
/* 80 */ LS_ACS_Execute,
|
/* 80 */ LS_ACS_Execute,
|
||||||
/* 81 */ LS_ACS_Suspend,
|
/* 81 */ LS_ACS_Suspend,
|
||||||
/* 82 */ LS_ACS_Terminate,
|
/* 82 */ LS_ACS_Terminate,
|
||||||
|
|
|
@ -361,65 +361,24 @@ void AActor::Serialize (FArchive &arc)
|
||||||
arc << foo;
|
arc << foo;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arc.IsStoring ())
|
if (SaveVersion > 2500) // adjust after merging into trunk!
|
||||||
{
|
{
|
||||||
int convnum = 0;
|
arc << ConversationRoot << Conversation;
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
if (Conversation != NULL)
|
|
||||||
{
|
|
||||||
for (i = 0; i < StrifeDialogues.Size(); ++i)
|
|
||||||
{
|
|
||||||
if (StrifeDialogues[i] == GetDefault()->Conversation)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (; i + convnum < StrifeDialogues.Size(); ++convnum)
|
|
||||||
{
|
|
||||||
if (StrifeDialogues[i + convnum] == Conversation)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i + convnum < StrifeDialogues.Size())
|
|
||||||
{
|
|
||||||
convnum++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
convnum = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arc.WriteCount (convnum);
|
|
||||||
}
|
}
|
||||||
else
|
else // old code which uses relative indexing.
|
||||||
{
|
{
|
||||||
int convnum;
|
int convnum;
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
convnum = arc.ReadCount();
|
convnum = arc.ReadCount();
|
||||||
if (convnum == 0 || GetDefault()->Conversation == NULL)
|
if (convnum == 0)
|
||||||
{
|
{
|
||||||
Conversation = NULL;
|
Conversation = NULL;
|
||||||
|
ConversationRoot = -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (i = 0; i < StrifeDialogues.Size(); ++i)
|
// This cannot be restored anymore.
|
||||||
{
|
I_Error("Cannot load old savegames with active dialogues");
|
||||||
if (StrifeDialogues[i] == GetDefault()->Conversation)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i + convnum <= StrifeDialogues.Size())
|
|
||||||
{
|
|
||||||
Conversation = StrifeDialogues[i + convnum - 1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Conversation = GetDefault()->Conversation;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3482,6 +3441,7 @@ bool AActor::UpdateWaterLevel (fixed_t oldz, bool dosplash)
|
||||||
return false; // we did the splash ourselves
|
return false; // we did the splash ourselves
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// P_SpawnMobj
|
// P_SpawnMobj
|
||||||
|
@ -3508,6 +3468,17 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t
|
||||||
|
|
||||||
actor = static_cast<AActor *>(const_cast<PClass *>(type)->CreateNew ());
|
actor = static_cast<AActor *>(const_cast<PClass *>(type)->CreateNew ());
|
||||||
|
|
||||||
|
// Set default dialogue
|
||||||
|
actor->ConversationRoot = GetConversation(actor->GetClass()->TypeName);
|
||||||
|
if (actor->ConversationRoot != -1)
|
||||||
|
{
|
||||||
|
actor->Conversation = StrifeDialogues[actor->ConversationRoot];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
actor->Conversation = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
actor->x = actor->PrevX = ix;
|
actor->x = actor->PrevX = ix;
|
||||||
actor->y = actor->PrevY = iy;
|
actor->y = actor->PrevY = iy;
|
||||||
actor->z = actor->PrevZ = iz;
|
actor->z = actor->PrevZ = iz;
|
||||||
|
|
260
src/p_udmf.cpp
260
src/p_udmf.cpp
|
@ -35,7 +35,6 @@
|
||||||
|
|
||||||
#include "r_data.h"
|
#include "r_data.h"
|
||||||
#include "p_setup.h"
|
#include "p_setup.h"
|
||||||
#include "sc_man.h"
|
|
||||||
#include "p_lnspec.h"
|
#include "p_lnspec.h"
|
||||||
#include "templates.h"
|
#include "templates.h"
|
||||||
#include "i_system.h"
|
#include "i_system.h"
|
||||||
|
@ -43,6 +42,7 @@
|
||||||
#include "r_sky.h"
|
#include "r_sky.h"
|
||||||
#include "g_level.h"
|
#include "g_level.h"
|
||||||
#include "v_palette.h"
|
#include "v_palette.h"
|
||||||
|
#include "p_udmf.h"
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
//
|
//
|
||||||
|
@ -124,6 +124,152 @@ extern TArray<int> linemap;
|
||||||
#define CHECK_N(f) if (!(namespace_bits&(f))) break;
|
#define CHECK_N(f) if (!(namespace_bits&(f))) break;
|
||||||
|
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Common parsing routines
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Skip a key or block
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
void UDMFParserBase::Skip()
|
||||||
|
{
|
||||||
|
if (developer) sc.ScriptMessage("Ignoring unknown key \"%s\".", sc.String);
|
||||||
|
if(sc.CheckToken('{'))
|
||||||
|
{
|
||||||
|
int level = 1;
|
||||||
|
while(sc.GetToken())
|
||||||
|
{
|
||||||
|
if (sc.TokenType == '}')
|
||||||
|
{
|
||||||
|
level--;
|
||||||
|
if(level == 0)
|
||||||
|
{
|
||||||
|
sc.UnGet();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (sc.TokenType == '{')
|
||||||
|
{
|
||||||
|
level++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sc.MustGetToken('=');
|
||||||
|
do
|
||||||
|
{
|
||||||
|
sc.MustGetAnyToken();
|
||||||
|
}
|
||||||
|
while(sc.TokenType != ';');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Parses a 'key = value' line of the map
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
FName UDMFParserBase::ParseKey(bool checkblock, bool *isblock)
|
||||||
|
{
|
||||||
|
sc.MustGetString();
|
||||||
|
FName key = sc.String;
|
||||||
|
if (checkblock)
|
||||||
|
{
|
||||||
|
if (sc.CheckToken('{'))
|
||||||
|
{
|
||||||
|
if (isblock) *isblock = true;
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
else if (isblock) *isblock = false;
|
||||||
|
}
|
||||||
|
sc.MustGetToken('=');
|
||||||
|
|
||||||
|
sc.Number = 0;
|
||||||
|
sc.Float = 0;
|
||||||
|
sc.MustGetAnyToken();
|
||||||
|
|
||||||
|
if (sc.TokenType == '+' || sc.TokenType == '-')
|
||||||
|
{
|
||||||
|
bool neg = (sc.TokenType == '-');
|
||||||
|
sc.MustGetAnyToken();
|
||||||
|
if (sc.TokenType != TK_IntConst && sc.TokenType != TK_FloatConst)
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("Numeric constant expected");
|
||||||
|
}
|
||||||
|
if (neg)
|
||||||
|
{
|
||||||
|
sc.Number = -sc.Number;
|
||||||
|
sc.Float = -sc.Float;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sc.TokenType == TK_StringConst)
|
||||||
|
{
|
||||||
|
parsedString = sc.String;
|
||||||
|
}
|
||||||
|
int savedtoken = sc.TokenType;
|
||||||
|
sc.MustGetToken(';');
|
||||||
|
sc.TokenType = savedtoken;
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Syntax checks
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
int UDMFParserBase::CheckInt(const char *key)
|
||||||
|
{
|
||||||
|
if (sc.TokenType != TK_IntConst)
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("Integer value expected for key '%s'", key);
|
||||||
|
}
|
||||||
|
return sc.Number;
|
||||||
|
}
|
||||||
|
|
||||||
|
double UDMFParserBase::CheckFloat(const char *key)
|
||||||
|
{
|
||||||
|
if (sc.TokenType != TK_IntConst && sc.TokenType != TK_FloatConst)
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("Floating point value expected for key '%s'", key);
|
||||||
|
}
|
||||||
|
return sc.Float;
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_t UDMFParserBase::CheckFixed(const char *key)
|
||||||
|
{
|
||||||
|
return FLOAT2FIXED(CheckFloat(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
angle_t UDMFParserBase::CheckAngle(const char *key)
|
||||||
|
{
|
||||||
|
return angle_t(CheckFloat(key) * ANGLE_90 / 90.);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UDMFParserBase::CheckBool(const char *key)
|
||||||
|
{
|
||||||
|
if (sc.TokenType == TK_True) return true;
|
||||||
|
if (sc.TokenType == TK_False) return false;
|
||||||
|
sc.ScriptMessage("Boolean value expected for key '%s'", key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *UDMFParserBase::CheckString(const char *key)
|
||||||
|
{
|
||||||
|
if (sc.TokenType != TK_StringConst)
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("String value expected for key '%s'", key);
|
||||||
|
}
|
||||||
|
return parsedString;
|
||||||
|
}
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
//
|
//
|
||||||
// Storage of UDMF user properties
|
// Storage of UDMF user properties
|
||||||
|
@ -233,15 +379,11 @@ fixed_t GetUDMFFixed(int type, int index, const char *key)
|
||||||
//
|
//
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
struct UDMFParser
|
class UDMFParser : public UDMFParserBase
|
||||||
{
|
{
|
||||||
FScanner sc;
|
|
||||||
FName namespc;
|
|
||||||
int namespace_bits;
|
|
||||||
bool isTranslated;
|
bool isTranslated;
|
||||||
bool isExtended;
|
bool isExtended;
|
||||||
bool floordrop;
|
bool floordrop;
|
||||||
FString parsedString;
|
|
||||||
|
|
||||||
TArray<line_t> ParsedLines;
|
TArray<line_t> ParsedLines;
|
||||||
TArray<side_t> ParsedSides;
|
TArray<side_t> ParsedSides;
|
||||||
|
@ -251,113 +393,13 @@ struct UDMFParser
|
||||||
|
|
||||||
FDynamicColormap *fogMap, *normMap;
|
FDynamicColormap *fogMap, *normMap;
|
||||||
|
|
||||||
|
public:
|
||||||
UDMFParser()
|
UDMFParser()
|
||||||
{
|
{
|
||||||
linemap.Clear();
|
linemap.Clear();
|
||||||
fogMap = normMap = NULL;
|
fogMap = normMap = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//===========================================================================
|
|
||||||
//
|
|
||||||
// Parses a 'key = value' line of the map
|
|
||||||
//
|
|
||||||
//===========================================================================
|
|
||||||
|
|
||||||
FName ParseKey()
|
|
||||||
{
|
|
||||||
sc.MustGetString();
|
|
||||||
FName key = sc.String;
|
|
||||||
sc.MustGetToken('=');
|
|
||||||
|
|
||||||
sc.Number = 0;
|
|
||||||
sc.Float = 0;
|
|
||||||
sc.MustGetAnyToken();
|
|
||||||
|
|
||||||
if (sc.TokenType == '+' || sc.TokenType == '-')
|
|
||||||
{
|
|
||||||
bool neg = (sc.TokenType == '-');
|
|
||||||
sc.MustGetAnyToken();
|
|
||||||
if (sc.TokenType != TK_IntConst && sc.TokenType != TK_FloatConst)
|
|
||||||
{
|
|
||||||
sc.ScriptMessage("Numeric constant expected");
|
|
||||||
}
|
|
||||||
if (neg)
|
|
||||||
{
|
|
||||||
sc.Number = -sc.Number;
|
|
||||||
sc.Float = -sc.Float;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sc.TokenType == TK_StringConst)
|
|
||||||
{
|
|
||||||
parsedString = sc.String;
|
|
||||||
}
|
|
||||||
int savedtoken = sc.TokenType;
|
|
||||||
sc.MustGetToken(';');
|
|
||||||
sc.TokenType = savedtoken;
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
//===========================================================================
|
|
||||||
//
|
|
||||||
// Syntax checks
|
|
||||||
//
|
|
||||||
//===========================================================================
|
|
||||||
|
|
||||||
int CheckInt(const char *key)
|
|
||||||
{
|
|
||||||
if (sc.TokenType != TK_IntConst)
|
|
||||||
{
|
|
||||||
sc.ScriptMessage("Integer value expected for key '%s'", key);
|
|
||||||
}
|
|
||||||
return sc.Number;
|
|
||||||
}
|
|
||||||
|
|
||||||
double CheckFloat(const char *key)
|
|
||||||
{
|
|
||||||
if (sc.TokenType != TK_IntConst && sc.TokenType != TK_FloatConst)
|
|
||||||
{
|
|
||||||
sc.ScriptMessage("Floating point value expected for key '%s'", key);
|
|
||||||
}
|
|
||||||
return sc.Float;
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_t CheckFixed(const char *key)
|
|
||||||
{
|
|
||||||
return FLOAT2FIXED(CheckFloat(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
angle_t CheckAngle(const char *key)
|
|
||||||
{
|
|
||||||
return angle_t(CheckFloat(key) * ANGLE_90 / 90.);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CheckBool(const char *key)
|
|
||||||
{
|
|
||||||
if (sc.TokenType == TK_True) return true;
|
|
||||||
if (sc.TokenType == TK_False) return false;
|
|
||||||
sc.ScriptMessage("Boolean value expected for key '%s'", key);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *CheckString(const char *key)
|
|
||||||
{
|
|
||||||
if (sc.TokenType != TK_StringConst)
|
|
||||||
{
|
|
||||||
sc.ScriptMessage("String value expected for key '%s'", key);
|
|
||||||
}
|
|
||||||
return parsedString;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void Flag(T &value, int mask, const char *key)
|
|
||||||
{
|
|
||||||
if (CheckBool(key))
|
|
||||||
value |= mask;
|
|
||||||
else
|
|
||||||
value &= ~mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AddUserKey(FName key, int kind, int index)
|
void AddUserKey(FName key, int kind, int index)
|
||||||
{
|
{
|
||||||
FUDMFKeys &keyarray = UDMFKeys[kind][index];
|
FUDMFKeys &keyarray = UDMFKeys[kind][index];
|
||||||
|
@ -1457,6 +1499,10 @@ struct UDMFParser
|
||||||
ParseVertex(&vt);
|
ParseVertex(&vt);
|
||||||
ParsedVertices.Push(vt);
|
ParsedVertices.Push(vt);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Skip();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the real vertices
|
// Create the real vertices
|
||||||
|
|
38
src/p_udmf.h
Normal file
38
src/p_udmf.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef __P_UDMF_H
|
||||||
|
#define __P_UDMF_H
|
||||||
|
|
||||||
|
#include "sc_man.h"
|
||||||
|
#include "m_fixed.h"
|
||||||
|
#include "tables.h"
|
||||||
|
|
||||||
|
class UDMFParserBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
FScanner sc;
|
||||||
|
FName namespc;
|
||||||
|
int namespace_bits;
|
||||||
|
FString parsedString;
|
||||||
|
|
||||||
|
void Skip();
|
||||||
|
FName ParseKey(bool checkblock = false, bool *isblock = NULL);
|
||||||
|
int CheckInt(const char *key);
|
||||||
|
double CheckFloat(const char *key);
|
||||||
|
fixed_t CheckFixed(const char *key);
|
||||||
|
angle_t CheckAngle(const char *key);
|
||||||
|
bool CheckBool(const char *key);
|
||||||
|
const char *CheckString(const char *key);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void Flag(T &value, int mask, const char *key)
|
||||||
|
{
|
||||||
|
if (CheckBool(key))
|
||||||
|
value |= mask;
|
||||||
|
else
|
||||||
|
value &= ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BLOCK_ID (ENamedName)-1
|
||||||
|
|
||||||
|
#endif
|
505
src/p_usdf.cpp
Normal file
505
src/p_usdf.cpp
Normal file
|
@ -0,0 +1,505 @@
|
||||||
|
//
|
||||||
|
// p_usdf.cpp
|
||||||
|
//
|
||||||
|
// USDF dialogue parser
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
// Copyright (c) 2010
|
||||||
|
// Braden "Blzut3" Obrzut <admin@maniacsvault.net>
|
||||||
|
// Christoph Oelckers
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
// * Neither the name of the <organization> nor the
|
||||||
|
// names of its contributors may be used to endorse or promote products
|
||||||
|
// derived from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
// ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||||
|
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "r_data.h"
|
||||||
|
#include "p_setup.h"
|
||||||
|
#include "p_lnspec.h"
|
||||||
|
#include "templates.h"
|
||||||
|
#include "i_system.h"
|
||||||
|
#include "p_conversation.h"
|
||||||
|
#include "p_udmf.h"
|
||||||
|
#include "doomerrors.h"
|
||||||
|
|
||||||
|
#define Zd 1
|
||||||
|
#define St 2
|
||||||
|
|
||||||
|
class USDFParser : public UDMFParserBase
|
||||||
|
{
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Checks an actor type (different representation depending on manespace)
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
const PClass *CheckActorType(const char *key)
|
||||||
|
{
|
||||||
|
if (namespace_bits == St)
|
||||||
|
{
|
||||||
|
return GetStrifeType(CheckInt(key));
|
||||||
|
}
|
||||||
|
else if (namespace_bits == Zd)
|
||||||
|
{
|
||||||
|
const PClass *cls = PClass::FindClass(CheckString(key));
|
||||||
|
if (cls == NULL)
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("Unknown actor class '%s'", key);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!cls->IsDescendantOf(RUNTIME_CLASS(AActor)))
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("'%s' is not an actor type", key);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Parse a cost block
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
bool ParseCost(FStrifeDialogueReply *response)
|
||||||
|
{
|
||||||
|
FStrifeDialogueItemCheck check;
|
||||||
|
check.Item = NULL;
|
||||||
|
check.Amount = -1;
|
||||||
|
|
||||||
|
while (!sc.CheckToken('}'))
|
||||||
|
{
|
||||||
|
FName key = ParseKey();
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case NAME_Item:
|
||||||
|
check.Item = CheckActorType(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Amount:
|
||||||
|
check.Amount = CheckInt(key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response->ItemCheck.Push(check);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Parse a choice block
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
bool ParseChoice(FStrifeDialogueReply **&replyptr)
|
||||||
|
{
|
||||||
|
FStrifeDialogueReply *reply = new FStrifeDialogueReply;
|
||||||
|
memset(reply, 0, sizeof(*reply));
|
||||||
|
|
||||||
|
reply->Next = *replyptr;
|
||||||
|
*replyptr = reply;
|
||||||
|
replyptr = &reply->Next;
|
||||||
|
|
||||||
|
FString ReplyString;
|
||||||
|
FString QuickYes;
|
||||||
|
FString QuickNo;
|
||||||
|
FString LogString;
|
||||||
|
bool closeDialog = false;
|
||||||
|
|
||||||
|
|
||||||
|
reply->NeedsGold = false;
|
||||||
|
while (!sc.CheckToken('}'))
|
||||||
|
{
|
||||||
|
bool block = false;
|
||||||
|
int costs = 0;
|
||||||
|
FName key = ParseKey(true, &block);
|
||||||
|
if (!block)
|
||||||
|
{
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case NAME_Text:
|
||||||
|
ReplyString = CheckString(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Displaycost:
|
||||||
|
reply->NeedsGold = CheckBool(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Yesmessage:
|
||||||
|
QuickYes = CheckString(key);
|
||||||
|
//if (!QuickYes.Compare("_")) QuickYes = "";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Nomessage:
|
||||||
|
QuickNo = CheckString(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Log:
|
||||||
|
if (namespace_bits == St)
|
||||||
|
{
|
||||||
|
const char *s = CheckString(key);
|
||||||
|
if(strlen(s) < 4 || strnicmp(s, "LOG", 3) != 0)
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("Log must be in the format of LOG# to compile, ignoring.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reply->LogNumber = atoi(s + 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogString = CheckString(key);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Giveitem:
|
||||||
|
reply->GiveType = CheckActorType(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Nextpage:
|
||||||
|
reply->NextNode = CheckInt(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Closedialog:
|
||||||
|
closeDialog = CheckBool(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Special:
|
||||||
|
reply->ActionSpecial = CheckInt(key);
|
||||||
|
if (reply->ActionSpecial < 0 || reply->ActionSpecial > 255)
|
||||||
|
reply->ActionSpecial = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Arg0:
|
||||||
|
case NAME_Arg1:
|
||||||
|
case NAME_Arg2:
|
||||||
|
case NAME_Arg3:
|
||||||
|
case NAME_Arg4:
|
||||||
|
reply->Args[int(key)-int(NAME_Arg0)] = CheckInt(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case NAME_Cost:
|
||||||
|
ParseCost(reply);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
sc.UnGet();
|
||||||
|
Skip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Todo: Finalize
|
||||||
|
if (reply->ItemCheck.Size() > 0)
|
||||||
|
{
|
||||||
|
if (reply->ItemCheck[0].Amount <= 0) reply->NeedsGold = false;
|
||||||
|
if (reply->NeedsGold) ReplyString.AppendFormat(" for %u", reply->ItemCheck[0].Amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->Reply = ncopystring(ReplyString);
|
||||||
|
reply->QuickYes = ncopystring(QuickYes);
|
||||||
|
if (reply->ItemCheck.Size() > 0 && reply->ItemCheck[0].Item != NULL)
|
||||||
|
{
|
||||||
|
reply->QuickNo = ncopystring(QuickNo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reply->QuickNo = NULL;
|
||||||
|
}
|
||||||
|
reply->LogString = ncopystring(LogString);
|
||||||
|
if(!closeDialog) reply->NextNode *= -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Parse an ifitem block
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
bool ParseIfItem(FStrifeDialogueNode *node)
|
||||||
|
{
|
||||||
|
FStrifeDialogueItemCheck check;
|
||||||
|
check.Item = NULL;
|
||||||
|
check.Amount = -1;
|
||||||
|
|
||||||
|
while (!sc.CheckToken('}'))
|
||||||
|
{
|
||||||
|
FName key = ParseKey();
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case NAME_Item:
|
||||||
|
check.Item = CheckActorType(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Count:
|
||||||
|
// Not yet implemented in the engine. Todo later
|
||||||
|
check.Amount = CheckInt(key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node->ItemCheck.Push(check);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Parse a page block
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
bool ParsePage()
|
||||||
|
{
|
||||||
|
FStrifeDialogueNode *node = new FStrifeDialogueNode;
|
||||||
|
FStrifeDialogueReply **replyptr = &node->Children;
|
||||||
|
memset(node, 0, sizeof(*node));
|
||||||
|
//node->ItemCheckCount[0] = node->ItemCheckCount[1] = node->ItemCheckCount[2] = -1;
|
||||||
|
|
||||||
|
node->ThisNodeNum = StrifeDialogues.Push(node);
|
||||||
|
|
||||||
|
FString SpeakerName;
|
||||||
|
FString Dialogue;
|
||||||
|
|
||||||
|
while (!sc.CheckToken('}'))
|
||||||
|
{
|
||||||
|
bool block = false;
|
||||||
|
FName key = ParseKey(true, &block);
|
||||||
|
if (!block)
|
||||||
|
{
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case NAME_Name:
|
||||||
|
SpeakerName = CheckString(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Panel:
|
||||||
|
node->Backdrop = TexMan.CheckForTexture (CheckString(key), FTexture::TEX_MiscPatch);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Voice:
|
||||||
|
{
|
||||||
|
FString soundname = (namespace_bits == St? "svox/" : "");
|
||||||
|
const char * name = CheckString(key);
|
||||||
|
if (name[0] != 0)
|
||||||
|
{
|
||||||
|
soundname += name;
|
||||||
|
node->SpeakerVoice = FSoundID(S_FindSound(soundname));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Dialog:
|
||||||
|
Dialogue = CheckString(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Drop:
|
||||||
|
node->DropType = CheckActorType(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Link:
|
||||||
|
node->ItemCheckNode = CheckInt(key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case NAME_Ifitem:
|
||||||
|
if (!ParseIfItem(node)) return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Choice:
|
||||||
|
if (!ParseChoice(replyptr)) return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
sc.UnGet();
|
||||||
|
Skip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node->SpeakerName = ncopystring(SpeakerName);
|
||||||
|
node->Dialogue = ncopystring(Dialogue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Parse a conversation block
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
bool ParseConversation()
|
||||||
|
{
|
||||||
|
const PClass *type = NULL;
|
||||||
|
int dlgid = -1;
|
||||||
|
unsigned int startpos = StrifeDialogues.Size();
|
||||||
|
|
||||||
|
while (!sc.CheckToken('}'))
|
||||||
|
{
|
||||||
|
bool block = false;
|
||||||
|
FName key = ParseKey(true, &block);
|
||||||
|
if (!block)
|
||||||
|
{
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case NAME_Actor:
|
||||||
|
type = CheckActorType(key);
|
||||||
|
if (namespace_bits == St)
|
||||||
|
{
|
||||||
|
dlgid = CheckInt(key);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_Id:
|
||||||
|
if (namespace_bits == Zd)
|
||||||
|
{
|
||||||
|
dlgid = CheckInt(key);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case NAME_Page:
|
||||||
|
if (!ParsePage()) return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
sc.UnGet();
|
||||||
|
Skip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type == NULL)
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("No valid actor type defined in conversation.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SetConversation(dlgid, type, startpos);
|
||||||
|
for(;startpos < StrifeDialogues.Size(); startpos++)
|
||||||
|
{
|
||||||
|
StrifeDialogues[startpos]->SpeakerType = type;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
//
|
||||||
|
// Parse an USDF lump
|
||||||
|
//
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool Parse(int lumpnum, FileReader *lump, int lumplen)
|
||||||
|
{
|
||||||
|
char *buffer = new char[lumplen];
|
||||||
|
lump->Read(buffer, lumplen);
|
||||||
|
sc.OpenMem(Wads.GetLumpFullName(lumpnum), buffer, lumplen);
|
||||||
|
delete [] buffer;
|
||||||
|
sc.SetCMode(true);
|
||||||
|
// Namespace must be the first field because everything else depends on it.
|
||||||
|
if (sc.CheckString("namespace"))
|
||||||
|
{
|
||||||
|
sc.MustGetToken('=');
|
||||||
|
sc.MustGetToken(TK_StringConst);
|
||||||
|
namespc = sc.String;
|
||||||
|
switch(namespc)
|
||||||
|
{
|
||||||
|
case NAME_ZDoom:
|
||||||
|
namespace_bits = Zd;
|
||||||
|
break;
|
||||||
|
case NAME_Strife:
|
||||||
|
namespace_bits = St;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sc.ScriptMessage("Unknown namespace %s. Ignoring dialogue lump.\n", sc.String);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sc.MustGetToken(';');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sc.ScriptMessage("Map does not define a namespace.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sc.GetString())
|
||||||
|
{
|
||||||
|
if (sc.Compare("conversation"))
|
||||||
|
{
|
||||||
|
sc.MustGetToken('{');
|
||||||
|
if (!ParseConversation()) return false;
|
||||||
|
}
|
||||||
|
else if (sc.Compare("include"))
|
||||||
|
{
|
||||||
|
sc.MustGetToken('=');
|
||||||
|
sc.MustGetToken(TK_StringConst);
|
||||||
|
LoadScriptFile(sc.String, true);
|
||||||
|
sc.MustGetToken(';');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Skip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool P_ParseUSDF(int lumpnum, FileReader *lump, int lumplen)
|
||||||
|
{
|
||||||
|
USDFParser parse;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!parse.Parse(lumpnum, lump, lumplen))
|
||||||
|
{
|
||||||
|
// clean up the incomplete dialogue structures here
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(CRecoverableError &err)
|
||||||
|
{
|
||||||
|
Printf("%s", err.GetMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -376,17 +376,17 @@ void player_t::SetLogNumber (int num)
|
||||||
data[length]=0;
|
data[length]=0;
|
||||||
SetLogText (data);
|
SetLogText (data);
|
||||||
delete[] data;
|
delete[] data;
|
||||||
|
|
||||||
// Print log text to console
|
|
||||||
AddToConsole(-1, TEXTCOLOR_GOLD);
|
|
||||||
AddToConsole(-1, LogText);
|
|
||||||
AddToConsole(-1, "\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void player_t::SetLogText (const char *text)
|
void player_t::SetLogText (const char *text)
|
||||||
{
|
{
|
||||||
LogText = text;
|
LogText = text;
|
||||||
|
|
||||||
|
// Print log text to console
|
||||||
|
AddToConsole(-1, TEXTCOLOR_GOLD);
|
||||||
|
AddToConsole(-1, LogText);
|
||||||
|
AddToConsole(-1, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int player_t::GetSpawnClass()
|
int player_t::GetSpawnClass()
|
||||||
|
|
|
@ -255,13 +255,11 @@ DEFINE_INFO_PROPERTY(conversationid, IiI, Actor)
|
||||||
if ((gameinfo.flags & (GI_SHAREWARE|GI_TEASER2)) == (GI_SHAREWARE|GI_TEASER2))
|
if ((gameinfo.flags & (GI_SHAREWARE|GI_TEASER2)) == (GI_SHAREWARE|GI_TEASER2))
|
||||||
convid=id2;
|
convid=id2;
|
||||||
|
|
||||||
if (convid==-1) return;
|
|
||||||
}
|
}
|
||||||
if (convid<0 || convid>1000)
|
|
||||||
{
|
bag.Info->ConversationID = convid;
|
||||||
I_Error ("ConversationID must be in the range [0,1000]");
|
if (convid <= 0) return; // 0 is not usable because the dialogue scripts use it as 'no object'.
|
||||||
}
|
SetStrifeType(convid, info->Class);
|
||||||
else StrifeTypes[convid] = info->Class;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
|
@ -1662,7 +1662,6 @@ ACTOR TargetPractice 208
|
||||||
ACTOR ForceFieldGuard 25 native
|
ACTOR ForceFieldGuard 25 native
|
||||||
{
|
{
|
||||||
Game Strife
|
Game Strife
|
||||||
ConversationID 0
|
|
||||||
Health 10
|
Health 10
|
||||||
Radius 2
|
Radius 2
|
||||||
Height 1
|
Height 1
|
||||||
|
|
|
@ -900,6 +900,10 @@
|
||||||
RelativePath=".\src\p_udmf.cpp"
|
RelativePath=".\src\p_udmf.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\src\p_usdf.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\src\p_user.cpp"
|
RelativePath=".\src\p_user.cpp"
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in a new issue