mirror of
https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git
synced 2025-07-14 14:37:41 +00:00
Compare commits
140 Commits
OPENURU_BU
...
master
Author | SHA1 | Date | |
---|---|---|---|
76ef5e0111 | |||
618fb8f13e | |||
96facaa7bb | |||
883495c35c | |||
e1306da736 | |||
8d1d813df5 | |||
65d50895bb | |||
6a41bdbee7 | |||
3dcf1c2f26 | |||
70449244f4 | |||
82abcb2f07 | |||
870d0534df | |||
1f09d6c48a | |||
66ce08ed67 | |||
1c1d3dd82e | |||
4924156ad4 | |||
13ea0a98de | |||
f9723462d7 | |||
7d3774a732 | |||
ea7e4b2ab5 | |||
5d5ba00f7d | |||
af38137822 | |||
4b0ab75a79 | |||
dcb0309a08 | |||
7dde0855dc | |||
44c9b0694d | |||
dedf04ac43 | |||
eeb458817f | |||
68c7187155 | |||
6fa695a2c3 | |||
2a3c1b32ef | |||
d3cbaa94ef | |||
e7758ccc10 | |||
07b92234ad | |||
7dfa2ba56a | |||
1a091b7019 | |||
104a5ef4ee | |||
19cfaaaea9 | |||
dafd190083 | |||
fdee5a921b | |||
b322ee094c | |||
54c768d966 | |||
82159bcb4a | |||
b81eb21145 | |||
e7b4f5c6ba | |||
202e846a56 | |||
deb4858f3e | |||
bff879abfd | |||
fededb574b | |||
126e7a76dd | |||
26807a8d87 | |||
f54f14328e | |||
311671e7a2 | |||
8aaa98b8b7 | |||
3489e04463 | |||
33bbc490be | |||
fd0c2a205e | |||
b2521e0777 | |||
1f7de46bc2 | |||
f9d629cfc5 | |||
b16de9d86f | |||
aba5e2562c | |||
bff8203fbe | |||
23ded3210f | |||
bbeb25d29a | |||
9f9d510958 | |||
f0d2bd3eaa | |||
41627d1c7c | |||
ea4bb43623 | |||
40bcaba9d8 | |||
e93db035ba | |||
94fab5996f | |||
da7d2d0e5c | |||
2e8dc81bf3 | |||
c0e83259f3 | |||
838e3e51ad | |||
40f200e066 | |||
6e4996e1be | |||
2f1bb87056 | |||
df848b48c4 | |||
75c570877a | |||
e512dd0c68 | |||
4c76549fb3 | |||
1a930cb47f | |||
b9af23e458 | |||
9e2e2d02e1 | |||
7a73c2d8e3 | |||
933cbe2fe7 | |||
fe88f2e8a2 | |||
9e308cbebe | |||
126d7511e0 | |||
64323c6073 | |||
3b17e20fb9 | |||
8a2534dff6 | |||
25ea208084 | |||
a392a37fcd | |||
7c64654e1f | |||
9a1b37e7f6 | |||
1012251d9a | |||
1d65c78097 | |||
c50bb08e8d | |||
229883654e | |||
a82188cbf4 | |||
f90ab01d44 | |||
afed53f920 | |||
b0511ea068 | |||
dfc89b363c | |||
9ab07771df | |||
51b6b4750b | |||
69dac251b6 | |||
fd260827aa | |||
e81af06584 | |||
da5ba1062b | |||
5f3a8644a7 | |||
8291326614 | |||
90e67f6206 | |||
a974c9f1b2 | |||
f05d71a214 | |||
a317ae4b08 | |||
62089be53f | |||
391bbaa4e7 | |||
84eedce685 | |||
924152da98 | |||
8a42f86dc2 | |||
587414dfa5 | |||
b00c7729cc | |||
916bf4f87f | |||
2160831221 | |||
e8e9cd29ee | |||
c9fddca6d5 | |||
3f8eaf6a9a | |||
0ce35e84b2 | |||
31156794ad | |||
fc79b5f45a | |||
542711c7c0 | |||
b9f8e3d1bd | |||
224ad908cc | |||
ed516b6508 | |||
4329212d98 | |||
8ff71fb384 |
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug_Internal|Win32">
|
||||
@ -121,6 +121,8 @@
|
||||
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||
<MapExports>true</MapExports>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<HeapReserveSize>0x140000</HeapReserveSize>
|
||||
<StackReserveSize>0x140000</StackReserveSize>
|
||||
</Link>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
@ -177,6 +179,8 @@
|
||||
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||
<MapExports>true</MapExports>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<HeapReserveSize>0x140000</HeapReserveSize>
|
||||
<StackReserveSize>0x140000</StackReserveSize>
|
||||
</Link>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
@ -237,6 +241,8 @@
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<MapExports>true</MapExports>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<HeapReserveSize>0x140000</HeapReserveSize>
|
||||
<StackReserveSize>0x140000</StackReserveSize>
|
||||
</Link>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
@ -298,6 +304,8 @@
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<MapExports>true</MapExports>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<HeapReserveSize>0x140000</HeapReserveSize>
|
||||
<StackReserveSize>0x140000</StackReserveSize>
|
||||
</Link>
|
||||
<Midl>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
@ -802,4 +810,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -244,6 +244,7 @@
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release_Internal|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudioCore\plSrtFileReader.cpp" />
|
||||
<ClCompile Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudio\plOGGCodec.cpp">
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug_Internal|Win32'">Disabled</Optimization>
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
|
||||
@ -315,6 +316,7 @@
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudioCore\plAudioFileReader.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudioCore\plBufferedFileReader.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudioCore\plFastWavReader.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudioCore\plSrtFileReader.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudio\plOGGCodec.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudioCore\plSoundBuffer.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudioCore\plSoundDeswizzler.h" />
|
||||
|
@ -32,6 +32,9 @@
|
||||
<ClCompile Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudio\plWavFile.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudioCore\plSrtFileReader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudioCore\plAudioCore.h">
|
||||
@ -61,5 +64,8 @@
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudio\plWavFile.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plAudioCore\plSrtFileReader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -817,6 +817,7 @@
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plMessage\plSimStateMsg.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plMessage\plSpawnModMsg.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plMessage\plSpawnRequestMsg.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plMessage\plSubtitleMsg.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plMessage\plSwimMsg.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plMessage\plSynchEnableMsg.h" />
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plMessage\plTimerCallbackMsg.h" />
|
||||
|
@ -361,5 +361,8 @@
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plMessage\plVaultNotifyMsg.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\..\Sources\Plasma\PubUtilLib\plMessage\plSubtitleMsg.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -2313,6 +2313,7 @@ void plClient::IDetectAudioVideoSettings()
|
||||
WriteInt(stream, "Audio.SetChannelVolume Ambience", 1);
|
||||
WriteInt(stream, "Audio.SetChannelVolume NPCVoice", 1);
|
||||
WriteInt(stream, "Audio.EnableVoiceRecording", 1);
|
||||
WriteInt(stream, "Audio.EnableSubtitles", true);
|
||||
WriteString(stream, "Audio.SetDeviceName", deviceName );
|
||||
stream->Close();
|
||||
delete stream;
|
||||
|
@ -499,6 +499,22 @@ void plVirtualCam1::SetCutNextTrans()
|
||||
#endif
|
||||
}
|
||||
|
||||
void plVirtualCam1::SetCutNext()
|
||||
{
|
||||
plCameraModifier1* cam = GetCurrentCamera();
|
||||
if (cam && cam->GetBrain()) {
|
||||
cam->GetBrain()->SetFlags(plCameraBrain1::kCutPosOnce);
|
||||
cam->GetBrain()->SetFlags(plCameraBrain1::kCutPOAOnce);
|
||||
}
|
||||
|
||||
SetFlags(kCutNextTrans);
|
||||
SetRender(true);
|
||||
|
||||
#ifdef STATUS_LOG
|
||||
camLog->AddLineF("Set Camera to cut on next frame");
|
||||
#endif
|
||||
}
|
||||
|
||||
void plVirtualCam1::SetRender(hsBool render)
|
||||
{
|
||||
fFlags.SetBit(kRender,render);
|
||||
|
@ -85,6 +85,7 @@ public:
|
||||
enum flags
|
||||
{
|
||||
kSetFOV,
|
||||
/** Forces the next camera transition to be cut. */
|
||||
kCutNextTrans,
|
||||
kRender,
|
||||
kRegionIgnore,
|
||||
@ -145,6 +146,7 @@ public:
|
||||
hsPoint3 GetCameraPOA() { return fOutputPOA; }
|
||||
hsVector3 GetCameraUp() { return fOutputUp; }
|
||||
void SetCutNextTrans(); // used when player warps into a new camera region
|
||||
void SetCutNext();
|
||||
|
||||
const hsMatrix44 GetCurrentMatrix() { return fMatrix; }
|
||||
static plVirtualCam1* Instance() { return fInstance; }
|
||||
|
@ -3391,6 +3391,11 @@ PF_CONSOLE_CMD( Audio, MuteAll, "bool on", "Mute or unmute all sounds")
|
||||
plgAudioSys::SetMuted( (bool)params[ 0 ] );
|
||||
}
|
||||
|
||||
PF_CONSOLE_CMD(Audio, EnableSubtitles, "bool on", "Enable or disable displaying subtitles for audio files containing speech")
|
||||
{
|
||||
plgAudioSys::SetEnableSubtitles((bool)params[0]);
|
||||
}
|
||||
|
||||
PF_CONSOLE_CMD( Audio, SetDistanceModel, "int type", "Sets the distance model for all 3d sounds")
|
||||
{
|
||||
if(plgAudioSys::Sys())
|
||||
@ -7056,17 +7061,12 @@ PF_CONSOLE_CMD( Python,
|
||||
"string functions, ...", // Params
|
||||
"Run a cheat command" )
|
||||
{
|
||||
std::string extraParms;
|
||||
const char* extraParms = "";
|
||||
if (numParams > 1)
|
||||
{
|
||||
extraParms = "(";
|
||||
extraParms.append(params[1]);
|
||||
extraParms.append(",)");
|
||||
extraParms = params[1];
|
||||
}
|
||||
else
|
||||
extraParms = "()";
|
||||
|
||||
PythonInterface::RunFunctionSafe("xCheat", params[0], extraParms.c_str());
|
||||
PythonInterface::RunFunctionStringArg("xCheat", params[0], extraParms);
|
||||
|
||||
std::string output;
|
||||
// get the messages
|
||||
|
@ -165,6 +165,8 @@ class pfKIMsg : public plMessage
|
||||
kUNUSED1 = 0x00000008,
|
||||
kStatusMsg = 0x00000010,
|
||||
kNeighborMsg = 0x00000020, // sending to all the neighbors
|
||||
kSubtitleMsg = 0x00000040,
|
||||
kLocKeyMsg = 0x00000080,
|
||||
kChannelMask = 0x0000ff00
|
||||
};
|
||||
|
||||
|
@ -1978,3 +1978,12 @@ hsBool cyAvatar::IsCurrentBrainHuman()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cyAvatar::SetDontPanicLink(bool value)
|
||||
{
|
||||
if ( fRecvr.Count() > 0 ) {
|
||||
plArmatureMod* mod = plAvatarMgr::FindAvatar(fRecvr[0]);
|
||||
if (mod)
|
||||
mod->SetDontPanicLinkFlag(value);
|
||||
}
|
||||
}
|
||||
|
@ -566,6 +566,14 @@ public:
|
||||
|
||||
static hsBool IsCurrentBrainHuman();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Function : SetDontPanicLink
|
||||
// PARAMETERS : value
|
||||
//
|
||||
// PURPOSE : Disables panic linking to Personal Age (warps the avatar back to the start instead)
|
||||
//
|
||||
void SetDontPanicLink(bool value);
|
||||
};
|
||||
|
||||
#endif // cyAvatar_h
|
||||
|
@ -597,6 +597,18 @@ PYTHON_METHOD_DEFINITION(ptAvatar, playSimpleAnimation, args)
|
||||
PYTHON_RETURN_NONE;
|
||||
}
|
||||
|
||||
PYTHON_METHOD_DEFINITION(ptAvatar, setDontPanicLink, args)
|
||||
{
|
||||
bool value;
|
||||
if (!PyArg_ParseTuple(args, "b", &value)) {
|
||||
PyErr_SetString(PyExc_TypeError, "setDontPanicLink expects a boolean");
|
||||
PYTHON_RETURN_ERROR;
|
||||
}
|
||||
|
||||
self->fThis->SetDontPanicLink(value);
|
||||
PYTHON_RETURN_NONE;
|
||||
}
|
||||
|
||||
PYTHON_START_METHODS_TABLE(ptAvatar)
|
||||
PYTHON_METHOD(ptAvatar, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n"
|
||||
"- This is to be used if your Python program is running on only one client\n"
|
||||
@ -651,6 +663,8 @@ PYTHON_START_METHODS_TABLE(ptAvatar)
|
||||
PYTHON_METHOD(ptAvatar, unRegisterForBehaviorNotify, "Params: selfKey\nThis will unregister behavior notifications"),
|
||||
|
||||
PYTHON_METHOD(ptAvatar, playSimpleAnimation, "Params: animName\nPlay simple animation on avatar"),
|
||||
|
||||
PYTHON_METHOD(ptAvatar, setDontPanicLink, "Params: value\nDisables panic linking to Personal Age (warps the avatar back to the start instead)"),
|
||||
PYTHON_END_METHODS_TABLE;
|
||||
|
||||
PYTHON_GLOBAL_METHOD_DEFINITION(PtSetBehaviorLoopCount, args, "Params: behaviorKey,stage,loopCount,netForce\nThis will set the loop count for a particular stage in a multistage behavior")
|
||||
|
@ -2602,7 +2602,7 @@ void cyMisc::WearMaintainerSuit(pyKey& key, hsBool wear)
|
||||
|
||||
}
|
||||
|
||||
void cyMisc::WearDefaultClothing(pyKey& key)
|
||||
void cyMisc::WearDefaultClothing(pyKey& key, hsBool broadcast)
|
||||
{
|
||||
if (key.getKey() != plNetClientMgr::GetInstance()->GetLocalPlayerKey())
|
||||
return;
|
||||
@ -2611,11 +2611,11 @@ void cyMisc::WearDefaultClothing(pyKey& key)
|
||||
|
||||
if (avMod)
|
||||
{
|
||||
avMod->GetClothingOutfit()->WearDefaultClothing();
|
||||
avMod->GetClothingOutfit()->WearDefaultClothing(broadcast);
|
||||
}
|
||||
}
|
||||
|
||||
void cyMisc::WearDefaultClothingType(pyKey& key, UInt32 type)
|
||||
void cyMisc::WearDefaultClothingType(pyKey& key, UInt32 type, hsBool broadcast)
|
||||
{
|
||||
if (key.getKey() != plNetClientMgr::GetInstance()->GetLocalPlayerKey())
|
||||
return;
|
||||
@ -2624,7 +2624,7 @@ void cyMisc::WearDefaultClothingType(pyKey& key, UInt32 type)
|
||||
|
||||
if (avMod)
|
||||
{
|
||||
avMod->GetClothingOutfit()->WearDefaultClothingType(type);
|
||||
avMod->GetClothingOutfit()->WearDefaultClothingType(type, broadcast);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -875,8 +875,8 @@ public:
|
||||
//
|
||||
static void WearMaintainerSuit(pyKey& key, hsBool wear);
|
||||
|
||||
static void WearDefaultClothing(pyKey& key);
|
||||
static void WearDefaultClothingType(pyKey& key, UInt32 type);
|
||||
static void WearDefaultClothing(pyKey& key, hsBool broadcast = false);
|
||||
static void WearDefaultClothingType(pyKey& key, UInt32 type, hsBool broadcast = false);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -598,22 +598,24 @@ PYTHON_GLOBAL_METHOD_DEFINITION(PtFakeLinkAvatarToObject, args, "Params: avatar,
|
||||
PYTHON_RETURN_NONE;
|
||||
}
|
||||
|
||||
PYTHON_GLOBAL_METHOD_DEFINITION(PtWearDefaultClothingType, args, "Params: key,type\nForces the avatar to wear the default clothing of the specified type")
|
||||
PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtWearDefaultClothingType, args, kw, "Params: key,type,broadcast=false\nForces the avatar to wear the default clothing of the specified type")
|
||||
{
|
||||
char* kwlist[] = { "key", "type", "broadcast", NULL };
|
||||
PyObject* keyObj = NULL;
|
||||
unsigned long type;
|
||||
if (!PyArg_ParseTuple(args, "Ol", &keyObj, &type))
|
||||
bool broadcast = false;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "Ol|b", kwlist, &keyObj, &type, &broadcast))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey and an unsigned long");
|
||||
PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey, an unsigned long, and an optional bool");
|
||||
PYTHON_RETURN_ERROR;
|
||||
}
|
||||
if (!pyKey::Check(keyObj))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey and an unsigned long");
|
||||
PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey, an unsigned long, and an optional bool");
|
||||
PYTHON_RETURN_ERROR;
|
||||
}
|
||||
pyKey* key = pyKey::ConvertFrom(keyObj);
|
||||
cyMisc::WearDefaultClothingType(*key, type);
|
||||
cyMisc::WearDefaultClothingType(*key, type, broadcast);
|
||||
PYTHON_RETURN_NONE;
|
||||
}
|
||||
|
||||
@ -775,4 +777,4 @@ void cyMisc::AddPlasmaMethods3(std::vector<PyMethodDef> &methods)
|
||||
|
||||
PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetUserPath);
|
||||
PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetInitPath);
|
||||
}
|
||||
}
|
||||
|
@ -529,21 +529,23 @@ PYTHON_GLOBAL_METHOD_DEFINITION(PtWearMaintainerSuit, args, "Params: key,wearOrN
|
||||
PYTHON_RETURN_NONE;
|
||||
}
|
||||
|
||||
PYTHON_GLOBAL_METHOD_DEFINITION(PtWearDefaultClothing, args, "Params: key\nForces the avatar to wear the default clothing set")
|
||||
PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtWearDefaultClothing, args, kw, "Params: key, broadcast=false\nForces the avatar to wear the default clothing set")
|
||||
{
|
||||
char* kwlist[] = { "key", "broadcast", NULL };
|
||||
PyObject* keyObj = NULL;
|
||||
if (!PyArg_ParseTuple(args, "O", &keyObj))
|
||||
bool broadcast = false;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "O|b", kwlist, &keyObj, &broadcast))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothing expects a ptKey");
|
||||
PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothing expects a ptKey and an optional bool");
|
||||
PYTHON_RETURN_ERROR;
|
||||
}
|
||||
if (!pyKey::Check(keyObj))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothing expects a ptKey");
|
||||
PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothing expects a ptKey and an optional bool");
|
||||
PYTHON_RETURN_ERROR;
|
||||
}
|
||||
pyKey* key = pyKey::ConvertFrom(keyObj);
|
||||
cyMisc::WearDefaultClothing(*key);
|
||||
cyMisc::WearDefaultClothing(*key, broadcast);
|
||||
PYTHON_RETURN_NONE;
|
||||
}
|
||||
|
||||
@ -851,4 +853,4 @@ void cyMisc::AddPlasmaMethods4(std::vector<PyMethodDef> &methods)
|
||||
PYTHON_GLOBAL_METHOD(methods, PtGetAIAvatarsByModelName);
|
||||
PYTHON_GLOBAL_METHOD(methods, PtForceVaultNodeUpdate);
|
||||
PYTHON_GLOBAL_METHOD(methods, PtVaultDownload);
|
||||
}
|
||||
}
|
||||
|
@ -2102,38 +2102,32 @@ PyObject* PythonInterface::RunFunction(PyObject* module, const char* name, PyObj
|
||||
return result;
|
||||
}
|
||||
|
||||
PyObject* PythonInterface::ParseArgs(const char* args)
|
||||
{
|
||||
PyObject* result = NULL;
|
||||
PyObject* scope = PyDict_New();
|
||||
if (scope)
|
||||
{
|
||||
//- Py_eval_input makes this function accept only single expresion (not statement)
|
||||
//- When using empty scope, functions and classes like 'file' or '__import__' are not visible
|
||||
result = PyRun_String(args, Py_eval_input, scope, NULL);
|
||||
Py_DECREF(scope);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool PythonInterface::RunFunctionSafe(const char* module, const char* function, const char* args)
|
||||
bool PythonInterface::RunFunctionStringArg(const char* module, const char* name, const char* arg)
|
||||
{
|
||||
PyObject* moduleObj = ImportModule(module);
|
||||
bool result = false;
|
||||
if (moduleObj)
|
||||
{
|
||||
PyObject* argsObj = ParseArgs(args);
|
||||
if (argsObj)
|
||||
PyObject* argObj = PyString_FromString(arg);
|
||||
if (argObj)
|
||||
{
|
||||
PyObject* callResult = RunFunction(moduleObj, function, argsObj);
|
||||
if (callResult)
|
||||
PyObject* argsObj = PyTuple_New(1);
|
||||
if (argsObj)
|
||||
{
|
||||
result = true;
|
||||
Py_DECREF(callResult);
|
||||
// PyTuple_SET_ITEM steals the reference to argObj.
|
||||
PyTuple_SET_ITEM(argsObj, 0, argObj);
|
||||
PyObject* callResult = RunFunction(moduleObj, name, argsObj);
|
||||
if (callResult)
|
||||
{
|
||||
result = true;
|
||||
Py_DECREF(callResult);
|
||||
}
|
||||
Py_DECREF(argsObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
Py_DECREF(argObj);
|
||||
}
|
||||
|
||||
Py_DECREF(argsObj);
|
||||
}
|
||||
Py_DECREF(moduleObj);
|
||||
}
|
||||
|
@ -223,9 +223,7 @@ public:
|
||||
|
||||
static PyObject* RunFunction(PyObject* module, const char* name, PyObject* args);
|
||||
|
||||
static PyObject* ParseArgs(const char* args);
|
||||
|
||||
static bool RunFunctionSafe(const char* module, const char* function, const char* args);
|
||||
static bool RunFunctionStringArg(const char* module, const char* name, const char* arg);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -94,6 +94,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
#include "../pfGameMgr/pfGameMgr.h"
|
||||
#include "../plMessage/plAIMsg.h"
|
||||
#include "../plAvatar/plAvBrainCritter.h"
|
||||
#include "../plMessage/plSubtitleMsg.h"
|
||||
|
||||
#include "plProfile.h"
|
||||
|
||||
@ -189,6 +190,7 @@ char* plPythonFileMod::fFunctionNames[] =
|
||||
{ "OnGameMgrMsg" }, // kfunc_OnGameMgrMsg
|
||||
{ "OnGameCliMsg" }, // kfunc_OnGameCliMsg
|
||||
{ "OnAIMsg" }, // kfunc_OnAIMsg
|
||||
{ "OnSubtitleMsg" }, // kfunc_OnSubtitleMsg
|
||||
{ nil }
|
||||
};
|
||||
|
||||
@ -828,6 +830,8 @@ void plPythonFileMod::AddTarget(plSceneObject* sobj)
|
||||
// the message that is spammed to anyone who will listen
|
||||
plgDispatch::Dispatch()->RegisterForExactType(plAIBrainCreatedMsg::Index(), GetKey());
|
||||
}
|
||||
if (fPyFunctionInstances[kfunc_OnSubtitleMsg])
|
||||
plgDispatch::Dispatch()->RegisterForExactType(plSubtitleMsg::Index(), GetKey());
|
||||
|
||||
// As the last thing... call the OnInit function if they have one
|
||||
if ( fPyFunctionInstances[kfunc_Init] != nil )
|
||||
@ -2780,6 +2784,36 @@ hsBool plPythonFileMod::MsgReceive(plMessage* msg)
|
||||
}
|
||||
}
|
||||
|
||||
if (fPyFunctionInstances[kfunc_OnSubtitleMsg])
|
||||
{
|
||||
plSubtitleMsg* pSubMsg = plSubtitleMsg::ConvertNoRef(msg);
|
||||
if (pSubMsg)
|
||||
{
|
||||
plProfile_BeginTiming(PythonUpdate);
|
||||
PyObject* retVal = PyObject_CallMethod(
|
||||
fPyFunctionInstances[kfunc_OnSubtitleMsg], fFunctionNames[kfunc_OnSubtitleMsg],
|
||||
"ss", pSubMsg->GetText().c_str(), pSubMsg->GetSpeaker().c_str());
|
||||
if (retVal == nil)
|
||||
{
|
||||
#ifndef PLASMA_EXTERNAL_RELEASE
|
||||
// for some reason this function didn't, remember that and not call it again
|
||||
fPyFunctionInstances[kfunc_OnSubtitleMsg] = nil;
|
||||
#endif //PLASMA_EXTERNAL_RELEASE
|
||||
// if there was an error make sure that the stderr gets flushed so it can be seen
|
||||
ReportError();
|
||||
}
|
||||
|
||||
Py_XDECREF(retVal);
|
||||
plProfile_EndTiming(PythonUpdate);
|
||||
|
||||
// display any output (NOTE: this would be disabled in production)
|
||||
DisplayPythonOutput();
|
||||
|
||||
// we handled this message (I think)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return plModifier::MsgReceive(msg);
|
||||
}
|
||||
|
||||
|
@ -202,6 +202,7 @@ public:
|
||||
kfunc_OnGameMgrMsg,
|
||||
kfunc_OnGameCliMsg,
|
||||
kfunc_OnAIMsg,
|
||||
kfunc_OnSubtitleMsg,
|
||||
kfunc_lastone
|
||||
};
|
||||
// array of matching Python instance where the functions are, if defined
|
||||
|
@ -474,6 +474,12 @@ void plPythonSDLModifier::IPythonVarToSDL(plStateDataRecord* state, const char*
|
||||
int count = PyTuple_Size(pyVar);
|
||||
plSimpleVarDescriptor::Type type = var->GetSimpleVarDescriptor()->GetType();
|
||||
|
||||
// Ensure that variable length arrays match.
|
||||
if (var->GetSimpleVarDescriptor()->IsVariableLength()) {
|
||||
if (var->GetCount() != count)
|
||||
var->Alloc(count);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
PyObject* pyVarItem = PyTuple_GetItem(pyVar, i);
|
||||
|
@ -256,6 +256,22 @@ hsBool pyAudioControl::IsMuted()
|
||||
return plgAudioSys::IsMuted();
|
||||
}
|
||||
|
||||
// Enable or disable displaying speech subtitles
|
||||
void pyAudioControl::EnableSubtitles()
|
||||
{
|
||||
plgAudioSys::SetEnableSubtitles(true);
|
||||
}
|
||||
|
||||
void pyAudioControl::DisableSubtitles()
|
||||
{
|
||||
plgAudioSys::SetEnableSubtitles(false);
|
||||
}
|
||||
|
||||
bool pyAudioControl::AreSubtitlesEnabled() const
|
||||
{
|
||||
return plgAudioSys::AreSubtitlesEnabled();
|
||||
}
|
||||
|
||||
hsBool pyAudioControl::SupportEAX(const char *deviceName)
|
||||
{
|
||||
return plgAudioSys::SupportsEAX(deviceName);
|
||||
|
@ -109,6 +109,11 @@ public:
|
||||
virtual void UnmuteAll();
|
||||
virtual hsBool IsMuted();
|
||||
|
||||
// Enable or disable displaying speech subtitles
|
||||
void EnableSubtitles();
|
||||
void DisableSubtitles();
|
||||
bool AreSubtitlesEnabled() const;
|
||||
|
||||
virtual void SetAudioSystemMode(int mode); // sets the current mode
|
||||
virtual int GetAudioSystemMode(); // returns the current mode
|
||||
virtual int GetHighestAudioMode(); // returns the highest mode the card is capable of handling
|
||||
|
@ -242,6 +242,14 @@ PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, isMuted)
|
||||
PYTHON_RETURN_BOOL(self->fThis->IsMuted());
|
||||
}
|
||||
|
||||
PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, enableSubtitles, EnableSubtitles)
|
||||
PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, disableSubtitles, DisableSubtitles)
|
||||
|
||||
PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, areSubtitlesEnabled)
|
||||
{
|
||||
PYTHON_RETURN_BOOL(self->fThis->AreSubtitlesEnabled());
|
||||
}
|
||||
|
||||
PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, canSetMicLevel)
|
||||
{
|
||||
PYTHON_RETURN_BOOL(self->fThis->CanSetMicLevel());
|
||||
@ -484,6 +492,9 @@ PYTHON_START_METHODS_TABLE(ptAudioControl)
|
||||
PYTHON_BASIC_METHOD(ptAudioControl, muteAll, "Mutes all sounds."),
|
||||
PYTHON_BASIC_METHOD(ptAudioControl, unmuteAll, "Unmutes all sounds."),
|
||||
PYTHON_METHOD_NOARGS(ptAudioControl, isMuted, "Are all sounds muted? Returns 1 if true otherwise returns 0."),
|
||||
PYTHON_BASIC_METHOD(ptAudioControl, enableSubtitles, "Enables audio subtitles."),
|
||||
PYTHON_BASIC_METHOD(ptAudioControl, disableSubtitles, "Disables audio subtitles."),
|
||||
PYTHON_METHOD_NOARGS(ptAudioControl, areSubtitlesEnabled, "Are audio subtitles enabled? Returns 1 if true otherwise returns 0."),
|
||||
PYTHON_METHOD_NOARGS(ptAudioControl, canSetMicLevel, "Can the microphone level be set? Returns 1 if true otherwise returns 0."),
|
||||
PYTHON_METHOD(ptAudioControl, setMicLevel, "Params: level\nSets the microphone recording level (0.0 to 1.0)."),
|
||||
PYTHON_METHOD_NOARGS(ptAudioControl, getMicLevel, "Returns the microphone recording level (0.0 to 1.0)."),
|
||||
|
@ -587,16 +587,17 @@ std::wstring pyGUIControlListBox::GetElementW( UInt16 idx )
|
||||
{
|
||||
// if its a text element type then it should be safe to cast it to a pfGUIListText
|
||||
pfGUIListText* letext = (pfGUIListText*)le;
|
||||
return letext->GetText();
|
||||
return (letext->GetText() != nullptr) ? letext->GetText() : L"";
|
||||
}
|
||||
else if ( le->GetType() == pfGUIListElement::kTreeRoot )
|
||||
{
|
||||
pfGUIListTreeRoot* elroot = (pfGUIListTreeRoot*)le;
|
||||
return elroot->GetTitle();
|
||||
return (elroot->GetTitle() != nullptr) ? elroot->GetTitle() : L"";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
|
@ -34,9 +34,9 @@ work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
|
||||
@ -46,19 +46,19 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
#include "plClassIndexMacros.h" // for CLASS_INDEX macro defn
|
||||
|
||||
CLASS_INDEX_LIST_START
|
||||
//---------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------
|
||||
// hsKeyedObjects should appear in the first section of the enum list
|
||||
// And everything else in the next section
|
||||
// Otherwise you will get an Assert
|
||||
//---------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------
|
||||
CLASS_INDEX(plSceneNode),
|
||||
CLASS_INDEX(plSceneObject),
|
||||
CLASS_INDEX(plSceneObject),
|
||||
CLASS_INDEX(hsKeyedObject),
|
||||
CLASS_INDEX(plBitmap),
|
||||
CLASS_INDEX(plMipmap),
|
||||
CLASS_INDEX(plCubicEnvironmap),
|
||||
CLASS_INDEX(plLayer),
|
||||
CLASS_INDEX(hsGMaterial),
|
||||
CLASS_INDEX(plLayer),
|
||||
CLASS_INDEX(hsGMaterial),
|
||||
CLASS_INDEX(plParticleSystem),
|
||||
CLASS_INDEX(plParticleEffect),
|
||||
CLASS_INDEX(plParticleCollisionEffectBeat),
|
||||
@ -74,7 +74,7 @@ CLASS_INDEX_LIST_START
|
||||
CLASS_INDEX(plWinAudible),
|
||||
CLASS_INDEX(plCoordinateInterface),
|
||||
CLASS_INDEX(plDrawInterface),
|
||||
CLASS_INDEX(plDrawable),
|
||||
CLASS_INDEX(plDrawable),
|
||||
CLASS_INDEX(plDrawableMesh),
|
||||
CLASS_INDEX(plDrawableIce),
|
||||
CLASS_INDEX(plPhysical),
|
||||
@ -131,7 +131,7 @@ CLASS_INDEX_LIST_START
|
||||
CLASS_INDEX(UNUSED_plDrawablePatchSet),
|
||||
CLASS_INDEX(plInputManager),
|
||||
CLASS_INDEX(plLogicModBase),
|
||||
CLASS_INDEX(plFogEnvironment),
|
||||
CLASS_INDEX(plFogEnvironment),
|
||||
CLASS_INDEX(plNetApp),
|
||||
CLASS_INDEX(plNetClientMgr),
|
||||
CLASS_INDEX(pl2WayWinAudible),
|
||||
@ -243,7 +243,7 @@ CLASS_INDEX_LIST_START
|
||||
CLASS_INDEX(pfGUIDynDisplayCtrl),
|
||||
CLASS_INDEX(UNUSED_plLayerProject),
|
||||
CLASS_INDEX(plInputInterfaceMgr),
|
||||
CLASS_INDEX(plRailCameraMod),
|
||||
CLASS_INDEX(plRailCameraMod),
|
||||
CLASS_INDEX(plMultistageBehMod),
|
||||
CLASS_INDEX(plCameraBrain1_Circle),
|
||||
CLASS_INDEX(plParticleWindEffect),
|
||||
@ -368,14 +368,15 @@ CLASS_INDEX_LIST_START
|
||||
CLASS_INDEX(plRidingAnimatedPhysicalDetector),
|
||||
CLASS_INDEX(plVolumeSensorConditionalObjectNoArbitration),
|
||||
CLASS_INDEX(plPXSubWorld),
|
||||
//---------------------------------------------------------
|
||||
// Keyed objects above this line, unkeyed (such as messages) below..
|
||||
//---------------------------------------------------------
|
||||
CLASS_INDEX(pfConfirmationMgr),
|
||||
//---------------------------------------------------------
|
||||
// Keyed objects above this line, unkeyed (such as messages) below..
|
||||
//---------------------------------------------------------
|
||||
|
||||
CLASS_INDEX_NONKEYED_OBJ_START
|
||||
CLASS_INDEX(plObjRefMsg),
|
||||
CLASS_INDEX(plObjRefMsg),
|
||||
CLASS_INDEX(plNodeRefMsg),
|
||||
CLASS_INDEX(plMessage),
|
||||
CLASS_INDEX(plMessage),
|
||||
CLASS_INDEX(plRefMsg),
|
||||
CLASS_INDEX(plGenRefMsg),
|
||||
CLASS_INDEX(plTimeMsg),
|
||||
@ -411,12 +412,12 @@ CLASS_INDEX_LIST_START
|
||||
CLASS_INDEX(hsSfxObjDistFade),
|
||||
CLASS_INDEX(hsSfxObjDistShade),
|
||||
CLASS_INDEX(hsDynamicValue),
|
||||
CLASS_INDEX(hsDynamicScalar),
|
||||
CLASS_INDEX(hsDynamicScalar),
|
||||
CLASS_INDEX(hsDynamicColorRGBA),
|
||||
CLASS_INDEX(hsDynamicMatrix33),
|
||||
CLASS_INDEX(hsDynamicMatrix44),
|
||||
CLASS_INDEX(plOmniSqApplicator),
|
||||
CLASS_INDEX(plPreResourceMsg),
|
||||
CLASS_INDEX(plPreResourceMsg),
|
||||
CLASS_INDEX(UNUSED_hsDynamicColorRGBA),
|
||||
CLASS_INDEX(UNUSED_hsDynamicMatrix33),
|
||||
CLASS_INDEX(UNUSED_hsDynamicMatrix44),
|
||||
@ -427,7 +428,7 @@ CLASS_INDEX_LIST_START
|
||||
CLASS_INDEX(UNUSED_plPosController),
|
||||
CLASS_INDEX(UNUSED_plScalarController),
|
||||
CLASS_INDEX(UNUSED_plPoint3Controller),
|
||||
CLASS_INDEX(UNUSED_plScaleValueController),
|
||||
CLASS_INDEX(UNUSED_plScaleValueController),
|
||||
CLASS_INDEX(UNUSED_plQuatController),
|
||||
CLASS_INDEX(UNUSED_plMatrix33Controller),
|
||||
CLASS_INDEX(UNUSED_plMatrix44Controller),
|
||||
@ -437,7 +438,7 @@ CLASS_INDEX_LIST_START
|
||||
CLASS_INDEX(plCompoundRotController),
|
||||
CLASS_INDEX(UNUSED_plSimplePosController),
|
||||
CLASS_INDEX(plCompoundPosController),
|
||||
CLASS_INDEX(plTMController),
|
||||
CLASS_INDEX(plTMController),
|
||||
CLASS_INDEX(hsFogControl),
|
||||
CLASS_INDEX(plIntRefMsg),
|
||||
CLASS_INDEX(plCollisionReactor),
|
||||
@ -577,8 +578,8 @@ CLASS_INDEX_LIST_START
|
||||
CLASS_INDEX(plNetServerMsgPing),
|
||||
CLASS_INDEX(plNetMsgAlive),
|
||||
CLASS_INDEX(plNetMsgTerminated),
|
||||
CLASS_INDEX(plSDLModifierMsg),
|
||||
CLASS_INDEX(plNetMsgSDLState),
|
||||
CLASS_INDEX(plSDLModifierMsg),
|
||||
CLASS_INDEX(plNetMsgSDLState),
|
||||
CLASS_INDEX(plNetServerMsgSessionReset),
|
||||
CLASS_INDEX(plCCRBanLinkingMsg),
|
||||
CLASS_INDEX(plCCRSilencePlayerMsg),
|
||||
@ -780,17 +781,17 @@ CLASS_INDEX_LIST_START
|
||||
CLASS_INDEX(plShadowCastMsg),
|
||||
CLASS_INDEX(plBoundsIsect),
|
||||
CLASS_INDEX(plResMgrHelperMsg),
|
||||
CLASS_INDEX(plNetCommAuthMsg),
|
||||
CLASS_INDEX(plNetCommFileListMsg),
|
||||
CLASS_INDEX(plNetCommFileDownloadMsg),
|
||||
CLASS_INDEX(plNetCommLinkToAgeMsg),
|
||||
CLASS_INDEX(plNetCommPlayerListMsg),
|
||||
CLASS_INDEX(plNetCommActivePlayerMsg),
|
||||
CLASS_INDEX(plNetCommCreatePlayerMsg),
|
||||
CLASS_INDEX(plNetCommDeletePlayerMsg),
|
||||
CLASS_INDEX(plNetCommPublicAgeListMsg),
|
||||
CLASS_INDEX(plNetCommPublicAgeMsg),
|
||||
CLASS_INDEX(plNetCommRegisterAgeMsg),
|
||||
CLASS_INDEX(plNetCommAuthMsg),
|
||||
CLASS_INDEX(plNetCommFileListMsg),
|
||||
CLASS_INDEX(plNetCommFileDownloadMsg),
|
||||
CLASS_INDEX(plNetCommLinkToAgeMsg),
|
||||
CLASS_INDEX(plNetCommPlayerListMsg),
|
||||
CLASS_INDEX(plNetCommActivePlayerMsg),
|
||||
CLASS_INDEX(plNetCommCreatePlayerMsg),
|
||||
CLASS_INDEX(plNetCommDeletePlayerMsg),
|
||||
CLASS_INDEX(plNetCommPublicAgeListMsg),
|
||||
CLASS_INDEX(plNetCommPublicAgeMsg),
|
||||
CLASS_INDEX(plNetCommRegisterAgeMsg),
|
||||
CLASS_INDEX(plVaultAdminInitializationTask),
|
||||
CLASS_INDEX(plMultistageModMsg),
|
||||
CLASS_INDEX(plSoundVolumeApplicator),
|
||||
@ -948,6 +949,17 @@ CLASS_INDEX_LIST_START
|
||||
CLASS_INDEX(plAngularVelocityMsg),
|
||||
CLASS_INDEX(plRideAnimatedPhysMsg),
|
||||
CLASS_INDEX(plAvBrainRideAnimatedPhysical),
|
||||
CLASS_INDEX(pfGameScoreMsg), // This index and below are unused currently and placed for indexing plSubtitleMsg consistently with H'uru
|
||||
CLASS_INDEX(pfGameScoreListMsg),
|
||||
CLASS_INDEX(pfGameScoreTransferMsg),
|
||||
CLASS_INDEX(pfGameScoreUpdateMsg),
|
||||
CLASS_INDEX(plLoadClothingMsg),
|
||||
CLASS_INDEX(plNullPipeline),
|
||||
CLASS_INDEX(plGLPipeline),
|
||||
CLASS_INDEX(plSDLModifierStateMsg),
|
||||
CLASS_INDEX(plConfirmationMsg),
|
||||
CLASS_INDEX(plLocalizedConfirmationMsg), // This index and above are unused currently and placed for indexing plSubtitleMsg consistently with H'uru
|
||||
CLASS_INDEX(plSubtitleMsg),
|
||||
CLASS_INDEX_LIST_END
|
||||
|
||||
#endif // plCreatableIndex_inc
|
||||
#endif // plCreatableIndex_inc
|
@ -43,7 +43,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
#ifndef plLogicModBase_inc
|
||||
#define plLogicModBase_inc
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include "plSingleModifier.h"
|
||||
#include "../pnNetCommon/plSynchedValue.h"
|
||||
#include "hsTemplates.h"
|
||||
|
@ -994,6 +994,7 @@ hsBool plgAudioSys::fInit = false;
|
||||
hsBool plgAudioSys::fActive = false;
|
||||
hsBool plgAudioSys::fUseHardware = false;
|
||||
hsBool plgAudioSys::fMuted = true;
|
||||
bool plgAudioSys::fEnableSubtitles = true;
|
||||
hsBool plgAudioSys::fDelayedActivate = false;
|
||||
hsBool plgAudioSys::fEnableEAX = false;
|
||||
hsWindowHndl plgAudioSys::fWnd = nil;
|
||||
@ -1047,6 +1048,11 @@ void plgAudioSys::SetMuted( hsBool b )
|
||||
SetGlobalFadeVolume(1.0);
|
||||
}
|
||||
|
||||
void plgAudioSys::SetEnableSubtitles(bool b)
|
||||
{
|
||||
fEnableSubtitles = b;
|
||||
}
|
||||
|
||||
void plgAudioSys::SetUseHardware(hsBool b)
|
||||
{
|
||||
fUseHardware = b;
|
||||
|
@ -193,11 +193,13 @@ public:
|
||||
static void SetUseHardware(hsBool b);
|
||||
static void SetActive(hsBool b);
|
||||
static void SetMuted( hsBool b );
|
||||
static void SetEnableSubtitles(bool b);
|
||||
static void EnableEAX( hsBool b );
|
||||
static hsBool Active() { return fInit; }
|
||||
static void Shutdown();
|
||||
static void Activate(hsBool b);
|
||||
static hsBool IsMuted( void ) { return fMuted; }
|
||||
static bool AreSubtitlesEnabled() { return fEnableSubtitles; }
|
||||
static hsWindowHndl hWnd() { return fWnd; }
|
||||
static plAudioSystem* Sys() { return fSys; }
|
||||
static void Restart( void );
|
||||
@ -258,6 +260,7 @@ private:
|
||||
static hsBool fInit;
|
||||
static hsBool fActive;
|
||||
static hsBool fMuted;
|
||||
static bool fEnableSubtitles;
|
||||
static hsWindowHndl fWnd;
|
||||
static hsBool fUseHardware;
|
||||
static hsBool fDelayedActivate;
|
||||
|
@ -710,9 +710,9 @@ hsBool plDSoundBuffer::IsEAXAccelerated( void ) const
|
||||
|
||||
//// BytePosToMSecs //////////////////////////////////////////////////////////
|
||||
|
||||
UInt32 plDSoundBuffer::BytePosToMSecs( UInt32 bytePos ) const
|
||||
UInt32 plDSoundBuffer::BytePosToMSecs(UInt32 bytePos) const
|
||||
{
|
||||
return (UInt32)(bytePos * 1000 / (hsScalar)fBufferDesc->fAvgBytesPerSec);
|
||||
return (UInt32)(bytePos / ((float)fBufferDesc->fAvgBytesPerSec / 1000.0f));
|
||||
}
|
||||
|
||||
//// GetBufferBytePos ////////////////////////////////////////////////////////
|
||||
|
@ -53,10 +53,12 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
#include "plWavFile.h"
|
||||
|
||||
#include "../plAudible/plWinAudible.h"
|
||||
#include "../plAudioCore/plSrtFileReader.h"
|
||||
#include "../pnMessage/plEventCallbackMsg.h"
|
||||
#include "../pnMessage/plSoundMsg.h"
|
||||
#include "../plMessage/plSubtitleMsg.h"
|
||||
#include "../plNetMessage/plNetMessage.h"
|
||||
#include "../pnNetCommon/plNetApp.h"
|
||||
#include "../pnMessage/plSoundMsg.h"
|
||||
#include "../pnMessage/plEventCallbackMsg.h"
|
||||
#include "../plPipeline/plPlates.h"
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
|
||||
@ -108,6 +110,23 @@ void plWin32Sound::IFreeBuffers( void )
|
||||
|
||||
void plWin32Sound::Update()
|
||||
{
|
||||
plSoundBuffer* buf = GetDataBuffer();
|
||||
if (buf != nullptr) {
|
||||
plSrtFileReader* srtReader = buf->GetSrtReader();
|
||||
if (srtReader != nullptr) {
|
||||
uint32_t currentTimeMs = (uint32_t)(GetActualTimeSec() * 1000.0f);
|
||||
if (currentTimeMs <= srtReader->GetLastEntryEndTime()) {
|
||||
while (plSrtEntry* nextEntry = srtReader->GetNextEntryStartingBeforeTime(currentTimeMs)) {
|
||||
if (plgAudioSys::AreSubtitlesEnabled()) {
|
||||
// add a plSubtitleMsg to go... to whoever is listening (probably the KI)
|
||||
plSubtitleMsg* msg = new plSubtitleMsg(nextEntry->GetSubtitleText(), nextEntry->GetSpeakerName());
|
||||
msg->Send();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plSound::Update();
|
||||
}
|
||||
|
||||
@ -120,6 +139,17 @@ void plWin32Sound::IActuallyPlay( void )
|
||||
{
|
||||
if (fDSoundBuffer && plgAudioSys::Active() )
|
||||
{
|
||||
if (!fReallyPlaying && fSynchedStartTimeSec > 0) {
|
||||
// advance past any subtitles that would end before the synched start time
|
||||
// not sure when this actually happens...
|
||||
plSoundBuffer* buf = GetDataBuffer();
|
||||
if (buf != nullptr) {
|
||||
plSrtFileReader* srtReader = buf->GetSrtReader();
|
||||
if (srtReader != nullptr) {
|
||||
srtReader->AdvanceToTime(fSynchedStartTimeSec * 1000.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sometimes base/derived classes can be annoying
|
||||
IDerivedActuallyPlay();
|
||||
|
@ -455,21 +455,24 @@ void plWin32StreamingSound::IActuallyStop()
|
||||
|
||||
unsigned plWin32StreamingSound::GetByteOffset()
|
||||
{
|
||||
if(fDataStream && fDSoundBuffer)
|
||||
{
|
||||
if (fDataStream && fDSoundBuffer)
|
||||
{
|
||||
UInt32 totalSize = fDataStream->GetDataSize();
|
||||
UInt32 bytesRemaining = fDataStream->NumBytesLeft();
|
||||
unsigned bytesQueued = fDSoundBuffer->BuffersQueued() * STREAM_BUFFER_SIZE;
|
||||
unsigned offset = fDSoundBuffer->GetByteOffset();
|
||||
long byteoffset = ((fDataStream->GetDataSize() - fDataStream->NumBytesLeft()) - bytesQueued) + offset;
|
||||
|
||||
return byteoffset < 0 ? fDataStream->GetDataSize() - abs(byteoffset) : byteoffset;
|
||||
long byteoffset = ((totalSize - bytesRemaining) - bytesQueued) + offset;
|
||||
|
||||
return byteoffset < 0 ? totalSize - std::abs(byteoffset) : byteoffset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
float plWin32StreamingSound::GetActualTimeSec()
|
||||
{
|
||||
if(fDataStream && fDSoundBuffer)
|
||||
return fDSoundBuffer->BytePosToMSecs(fDataStream->NumBytesLeft()) / 1000.0f;
|
||||
if (fDataStream && fDSoundBuffer)
|
||||
return fDSoundBuffer->BytePosToMSecs(this->GetByteOffset()) / 1000.0f;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
@ -45,9 +45,11 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
|
||||
#include "hsStream.h"
|
||||
#include "hsUtils.h"
|
||||
#include <cstring>
|
||||
|
||||
#include "plgDispatch.h"
|
||||
#include "hsResMgr.h"
|
||||
#include "plSrtFileReader.h"
|
||||
#include "../pnMessage/plRefMsg.h"
|
||||
#include "../plFile/plFileUtils.h"
|
||||
#include "../plFile/hsFiles.h"
|
||||
@ -117,20 +119,35 @@ static void LoadCallback(void *)
|
||||
else
|
||||
{
|
||||
plAudioFileReader *reader = nil;
|
||||
plSrtFileReader* srtReader = nullptr;
|
||||
|
||||
while(plSoundBuffer *buffer = templist.Head())
|
||||
{
|
||||
if(buffer->GetData())
|
||||
{
|
||||
reader = CreateReader(true, buffer->GetFileName(), buffer->GetAudioReaderType(), buffer->GetReaderSelect());
|
||||
const char* srcFilename = buffer->GetFileName();
|
||||
reader = CreateReader(true, srcFilename, buffer->GetAudioReaderType(), buffer->GetReaderSelect());
|
||||
|
||||
if( reader )
|
||||
{
|
||||
unsigned readLen = buffer->GetAsyncLoadLength() ? buffer->GetAsyncLoadLength() : buffer->GetDataLength();
|
||||
reader->Read( readLen, buffer->GetData() );
|
||||
buffer->SetAudioReader(reader); // give sound buffer reader, since we may need it later
|
||||
|
||||
plSrtFileReader* srtReader = buffer->GetSrtReader();
|
||||
if (srtReader != nullptr && (strcmp(srtReader->GetCurrentAudioFileName(), srcFilename) == 0)) {
|
||||
// same file we were playing before, so start the SRT feed over instead of deleting and reloading
|
||||
srtReader->StartOver();
|
||||
} else {
|
||||
std::unique_ptr<plSrtFileReader> newSrtFileReader(new plSrtFileReader(srcFilename));
|
||||
if (newSrtFileReader->ReadFile())
|
||||
buffer->SetSrtReader(newSrtFileReader.release());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->SetError();
|
||||
}
|
||||
}
|
||||
|
||||
templist.Unlink(buffer);
|
||||
@ -194,6 +211,8 @@ plSoundBuffer::~plSoundBuffer()
|
||||
ASSERT(!link.IsLinked());
|
||||
delete [] fFileName;
|
||||
UnLoad();
|
||||
|
||||
delete fSrtReader;
|
||||
}
|
||||
|
||||
void plSoundBuffer::IInitBuffer()
|
||||
@ -206,6 +225,7 @@ void plSoundBuffer::IInitBuffer()
|
||||
fFlags = 0;
|
||||
fDataRead = 0;
|
||||
fReader = nil;
|
||||
fSrtReader = nullptr;
|
||||
fLoaded = 0;
|
||||
fLoading = false;
|
||||
fHeader.fFormatTag = 0;
|
||||
@ -458,6 +478,12 @@ void plSoundBuffer::SetLoaded(bool loaded)
|
||||
fLoaded = loaded;
|
||||
}
|
||||
|
||||
void plSoundBuffer::SetSrtReader(plSrtFileReader* reader)
|
||||
{
|
||||
delete fSrtReader;
|
||||
fSrtReader = reader;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
|
@ -61,6 +61,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
|
||||
class plUnifiedTime;
|
||||
class plAudioFileReader;
|
||||
class plSrtFileReader;
|
||||
class plSoundBuffer : public hsKeyedObject
|
||||
{
|
||||
public:
|
||||
@ -118,6 +119,8 @@ public:
|
||||
plAudioFileReader * GetAudioReader(); // transfers ownership to caller
|
||||
void SetAudioReader(plAudioFileReader *reader);
|
||||
void SetLoaded(bool loaded);
|
||||
plSrtFileReader* GetSrtReader() const { return fSrtReader; } // does not transfer ownership
|
||||
void SetSrtReader(plSrtFileReader* reader);
|
||||
|
||||
plAudioFileReader::StreamType GetAudioReaderType() { return fStreamType; }
|
||||
unsigned GetAsyncLoadLength() { return fAsyncLoadLength ? fAsyncLoadLength : fDataLength; }
|
||||
@ -148,7 +151,8 @@ protected:
|
||||
bool fLoading;
|
||||
bool fError;
|
||||
|
||||
plAudioFileReader * fReader;
|
||||
plAudioFileReader * fReader;
|
||||
plSrtFileReader* fSrtReader;
|
||||
UInt8 * fData;
|
||||
plWAVHeader fHeader;
|
||||
UInt32 fDataLength;
|
||||
|
196
Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp
Normal file
196
Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
|
||||
#include "plSrtFileReader.h"
|
||||
|
||||
#include "hsStream.h"
|
||||
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
#include "../pnUtils/pnUtils.h"
|
||||
|
||||
#include <regex>
|
||||
|
||||
static const std::regex speakerTextRegex("^((([\\w.]+(\\s\\w+)?):) )?(.*)");
|
||||
static const std::regex timingsRegex("^(\\d{2}):(\\d{2}):(\\d{2}),(\\d{3}) --> (\\d{2}):(\\d{2}):(\\d{2}),(\\d{3})$");
|
||||
|
||||
bool plSrtFileReader::ReadFile()
|
||||
{
|
||||
wchar path[MAX_PATH];
|
||||
StrPrintf(path, arrsize(path), L"sfx\\%S", fAudioFileName);
|
||||
PathSetExtension(path, path, L".sub", arrsize(path));
|
||||
|
||||
if (PathDoesFileExist(path)) {
|
||||
// read sets of SRT data until end of file
|
||||
hsUNIXStream srtFileStream;
|
||||
|
||||
// if file exists and was opened successfully
|
||||
if (srtFileStream.Open(path, L"r")) {
|
||||
plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file %S", path);
|
||||
|
||||
uint32_t subtitleNumber = 0;
|
||||
uint32_t subtitleStartTimeMs = 0;
|
||||
uint32_t subtitleEndTimeMs = 0;
|
||||
char line[4096];
|
||||
std::string speakerName;
|
||||
std::string subtitleText;
|
||||
std::cmatch matches;
|
||||
|
||||
for (unsigned int lnCounter = 0; !srtFileStream.AtEnd(); lnCounter++) {
|
||||
if (lnCounter % 4 == 0 && srtFileStream.ReadLn(line)) {
|
||||
subtitleNumber = std::stoul(line);
|
||||
} else if (lnCounter % 4 == 1 && srtFileStream.ReadLn(line)) {
|
||||
if (std::regex_match(line, matches, timingsRegex)) {
|
||||
if (matches.size() < 9) {
|
||||
// add error message and ensure this subtitle line won't be added to the entries
|
||||
plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle timings '%s' are formatted incorrectly.", line);
|
||||
subtitleNumber = 0;
|
||||
}
|
||||
else {
|
||||
// matches[0] is the entire match, we don't do anything with it
|
||||
// matches[1] is the first group -- the start hour number
|
||||
subtitleStartTimeMs += std::stoul(matches[1].str()) * 3600000;
|
||||
// matches[2] is the second group -- the start minute number
|
||||
subtitleStartTimeMs += std::stoul(matches[2].str()) * 60000;
|
||||
// matches[3] is the third group -- the start seconds number
|
||||
subtitleStartTimeMs += std::stoul(matches[3].str()) * 1000;
|
||||
// matches[4] is the fourth group -- the start milliseconds number
|
||||
subtitleStartTimeMs += std::stoul(matches[4].str());
|
||||
|
||||
// matches[5] is the fifth group -- the end hour number
|
||||
subtitleEndTimeMs += std::stoul(matches[5].str()) * 3600000;
|
||||
// matches[6] is the sixth group -- the end minute number
|
||||
subtitleEndTimeMs += std::stoul(matches[6].str()) * 60000;
|
||||
// matches[7] is the seventh group -- the end seconds number
|
||||
subtitleEndTimeMs += std::stoul(matches[7].str()) * 1000;
|
||||
// matches[8] is the eighth group -- the end milliseconds number
|
||||
subtitleEndTimeMs += std::stoul(matches[8].str());
|
||||
}
|
||||
}
|
||||
} else if (lnCounter % 4 == 2 && srtFileStream.ReadLn(line)) {
|
||||
if (std::regex_match(line, matches, speakerTextRegex)) {
|
||||
if (matches.size() < 5) {
|
||||
// add error message and ensure this subtitle line won't be added to the entries
|
||||
plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle text and/or speaker name '%s' are formatted incorrectly.", line);
|
||||
subtitleNumber = 0;
|
||||
} else {
|
||||
// matches[0] is the entire match, we don't do anything with it
|
||||
// matches[2] is the second group (the optional subtitle speaker with colon but no space at the end)
|
||||
speakerName = matches[2];
|
||||
|
||||
// matches[5] is the fourth group (the subtitle text)
|
||||
subtitleText = matches[5];
|
||||
}
|
||||
}
|
||||
} else if (lnCounter % 4 == 3 && subtitleNumber > 0 && subtitleStartTimeMs >= 0 && subtitleEndTimeMs >= 0 && !subtitleText.empty()) {
|
||||
// entry is complete, add to the queue and reset our temp variables
|
||||
if (!speakerName.empty())
|
||||
fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText), std::move(speakerName)));
|
||||
else
|
||||
fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText)));
|
||||
|
||||
subtitleNumber = 0;
|
||||
subtitleStartTimeMs = 0;
|
||||
subtitleEndTimeMs = 0;
|
||||
subtitleText.clear();
|
||||
speakerName.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (subtitleNumber > 0 && subtitleStartTimeMs >= 0 && subtitleEndTimeMs >= 0 && !subtitleText.empty()) {
|
||||
// enqueue the last subtitle from the file if we didn't have an extra blank line at the end
|
||||
if (!speakerName.empty())
|
||||
fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText), std::move(speakerName)));
|
||||
else
|
||||
fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void plSrtFileReader::AdvanceToTime(uint32_t timeMs)
|
||||
{
|
||||
while (fCurrentEntryIndex < fEntries.size() && fEntries.at(fCurrentEntryIndex).GetEndTimeMs() <= timeMs) {
|
||||
fCurrentEntryIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
plSrtEntry* plSrtFileReader::GetNextEntryStartingBeforeTime(uint32_t timeMs)
|
||||
{
|
||||
if (fCurrentEntryIndex < fEntries.size()) {
|
||||
plSrtEntry& nextEntry = fEntries.at(fCurrentEntryIndex);
|
||||
|
||||
if (nextEntry.GetStartTimeMs() <= timeMs) {
|
||||
fCurrentEntryIndex++;
|
||||
return &nextEntry;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
plSrtEntry* plSrtFileReader::GetNextEntryEndingBeforeTime(uint32_t timeMs)
|
||||
{
|
||||
if (fCurrentEntryIndex < fEntries.size()) {
|
||||
plSrtEntry& nextEntry = fEntries.at(fCurrentEntryIndex);
|
||||
|
||||
if (nextEntry.GetEndTimeMs() <= timeMs) {
|
||||
fCurrentEntryIndex++;
|
||||
return &nextEntry;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t plSrtFileReader::GetLastEntryEndTime()
|
||||
{
|
||||
if (!fEntries.empty()) {
|
||||
return fEntries.back().GetEndTimeMs();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
108
Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h
Normal file
108
Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h
Normal file
@ -0,0 +1,108 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plSrtFileReader - Class for reading an SRT format file and //
|
||||
// storing the read entries for access later //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _plSrtFileReader_h
|
||||
#define _plSrtFileReader_h
|
||||
|
||||
#include "hsTypes.h"
|
||||
#include "../pnUtils/pnUtils.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
class plSrtEntry
|
||||
{
|
||||
public:
|
||||
|
||||
plSrtEntry(uint32_t entryNum, uint32_t startTimeMs, uint32_t endTimeMs, std::string subtitleText, std::string speakerName)
|
||||
: fEntryNum(entryNum), fStartTimeMs(startTimeMs), fEndTimeMs(endTimeMs), fSubtitleText(std::move(subtitleText)),
|
||||
fSpeakerName(std::move(speakerName)) { }
|
||||
|
||||
plSrtEntry(uint32_t entryNum, uint32_t startTimeMs, uint32_t endTimeMs, std::string subtitleText)
|
||||
: fEntryNum(entryNum), fStartTimeMs(startTimeMs), fEndTimeMs(endTimeMs), fSubtitleText(std::move(subtitleText)) { }
|
||||
|
||||
std::string GetSubtitleText() const { return fSubtitleText; }
|
||||
std::string GetSpeakerName() const { return fSpeakerName; }
|
||||
uint32_t GetStartTimeMs() const { return fStartTimeMs; }
|
||||
uint32_t GetEndTimeMs() const { return fEndTimeMs; }
|
||||
|
||||
protected:
|
||||
|
||||
uint32_t fEntryNum;
|
||||
uint32_t fStartTimeMs;
|
||||
uint32_t fEndTimeMs;
|
||||
std::string fSubtitleText;
|
||||
std::string fSpeakerName;
|
||||
|
||||
};
|
||||
|
||||
class plSrtFileReader
|
||||
{
|
||||
public:
|
||||
|
||||
plSrtFileReader(const char* audioFileName)
|
||||
: fAudioFileName(StrDup(audioFileName)), fCurrentEntryIndex(0) { }
|
||||
virtual ~plSrtFileReader() { delete[] fAudioFileName; }
|
||||
|
||||
bool ReadFile();
|
||||
void StartOver() { fCurrentEntryIndex = 0; }
|
||||
const char* GetCurrentAudioFileName() const { return fAudioFileName; }
|
||||
void AdvanceToTime(uint32_t timeMs);
|
||||
plSrtEntry* GetNextEntryStartingBeforeTime(uint32_t timeMs);
|
||||
plSrtEntry* GetNextEntryEndingBeforeTime(uint32_t timeMs);
|
||||
uint32_t GetLastEntryEndTime();
|
||||
|
||||
protected:
|
||||
|
||||
const char* fAudioFileName;
|
||||
std::vector<plSrtEntry> fEntries;
|
||||
uint32_t fCurrentEntryIndex;
|
||||
|
||||
};
|
||||
|
||||
#endif //_plSrtFileReader_h
|
@ -313,7 +313,7 @@ hsBool plAnimStage::ISendNotify(UInt32 notifyMask, UInt32 notifyType, plArmature
|
||||
int stageNum = genBrain ? genBrain->GetStageNum(this) : -1;
|
||||
msg->AddMultiStageEvent(stageNum, notifyType, armature->GetTarget(0)->GetKey());
|
||||
|
||||
if (! genBrain->RelayNotifyMsg(msg) )
|
||||
if (stageNum < 0 || !genBrain || !genBrain->RelayNotifyMsg(msg) )
|
||||
{
|
||||
msg->UnRef(); // couldn't send; destroy...
|
||||
}
|
||||
|
@ -865,10 +865,10 @@ void plArmatureMod::SpawnAt(int spawnNum, double time)
|
||||
w2l.RemoveScale();
|
||||
ci->SetTransform(l2w, w2l);
|
||||
ci->FlushTransform();
|
||||
|
||||
if (plVirtualCam1::Instance())
|
||||
plVirtualCam1::Instance()->SetCutNextTrans();
|
||||
|
||||
|
||||
if (IsLocalAvatar() && plVirtualCam1::Instance())
|
||||
plVirtualCam1::Instance()->SetCutNext();
|
||||
|
||||
if (GetFollowerParticleSystemSO())
|
||||
{
|
||||
// Since particles are in world space, if we've got some surrounding us, we've got to translate them to compensate for our warp.
|
||||
|
@ -234,7 +234,9 @@ public:
|
||||
virtual void PanicLink(hsBool playLinkOutAnim = true);
|
||||
virtual void PersonalLink();
|
||||
|
||||
virtual bool ToggleDontPanicLinkFlag() { fDontPanicLink = fDontPanicLink ? false : true; return fDontPanicLink; }
|
||||
bool ToggleDontPanicLinkFlag() { fDontPanicLink = fDontPanicLink ? false : true; return fDontPanicLink; }
|
||||
|
||||
void SetDontPanicLinkFlag(bool value) { fDontPanicLink = value; }
|
||||
|
||||
int GetBrainCount();
|
||||
plArmatureBrain *GetNextBrain(plArmatureBrain *brain);
|
||||
|
@ -326,7 +326,7 @@ bool plAvBrainClimb::IProcessExitStage(double time, float elapsed)
|
||||
|
||||
float curBlend = ai->GetBlend();
|
||||
|
||||
if(curBlend > .99) // reached peak strength
|
||||
if(fCurStage && curBlend > .99) // reached peak strength
|
||||
{
|
||||
fCurStage->Detach(fAvMod); // remove the (now completely masked) underlying anim
|
||||
fCurStage = nil;
|
||||
@ -703,8 +703,8 @@ void plAvBrainClimb::ICalcProbeLengths()
|
||||
{
|
||||
// we assume that the up and down climbs go the same distance;
|
||||
// same for the left and right climbs
|
||||
plAGAnim *up = fAvMod->FindCustomAnim("ClimbUp");
|
||||
plAGAnim *left = fAvMod->FindCustomAnim("ClimbLeft");
|
||||
plAGAnim *up = fAvMod->FindCustomAnim("WallClimbUp");
|
||||
plAGAnim *left = fAvMod->FindCustomAnim("WallClimbLeft");
|
||||
|
||||
hsMatrix44 upMove, leftMove;
|
||||
|
||||
|
@ -75,6 +75,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
#include "../plInputCore/plInputDevice.h"
|
||||
#include "../plMath/plRandom.h"
|
||||
#include "../plPipeline/plDebugText.h"
|
||||
#include "../plNetClient/plNetClientMgr.h"
|
||||
#include "../plNetClient/plNetLinkingMgr.h"
|
||||
|
||||
#include "../plMessage/plAvatarMsg.h"
|
||||
@ -494,12 +495,20 @@ hsBool plAvBrainHuman::IHandleClimbMsg(plClimbMsg *msg)
|
||||
bool isStartClimb = msg->fCommand == plClimbMsg::kStartClimbing;
|
||||
if(isStartClimb)
|
||||
{
|
||||
// let's build a seek task to get us to the attach point
|
||||
plKey seekTarget = msg->fTarget;
|
||||
plAvTaskSeek *seekTask = TRACKED_NEW plAvTaskSeek(seekTarget);
|
||||
QueueTask(seekTask);
|
||||
// Warp the player to the Seekpoint
|
||||
plSceneObject *avatarObj = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer());
|
||||
plSceneObject *obj = plSceneObject::ConvertNoRef(msg->fTarget->ObjectIsLoaded());
|
||||
plArmatureMod *localAvatar = plAvatarMgr::GetInstance()->GetLocalAvatar();
|
||||
plArmatureMod *climbAvatar = plArmatureMod::ConvertNoRef(fArmature);
|
||||
if (climbAvatar == localAvatar) // is it our avatar who has to seek?
|
||||
{
|
||||
hsMatrix44 target = obj->GetLocalToWorld();
|
||||
plWarpMsg *warp = TRACKED_NEW plWarpMsg(NULL, avatarObj->GetKey(), plWarpMsg::kFlushTransform, target);
|
||||
warp->SetBCastFlag(plMessage::kNetPropagate);
|
||||
plgDispatch::MsgSend(warp);
|
||||
}
|
||||
|
||||
// now a brain task to start the actual climb.
|
||||
// build the Climb brain
|
||||
plAvBrainClimb::Mode startMode;
|
||||
switch(msg->fDirection)
|
||||
{
|
||||
@ -515,10 +524,11 @@ hsBool plAvBrainHuman::IHandleClimbMsg(plClimbMsg *msg)
|
||||
case plClimbMsg::kRight:
|
||||
startMode = plAvBrainClimb::kMountingRight;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
plAvBrainClimb *brain = TRACKED_NEW plAvBrainClimb(startMode);
|
||||
plAvTaskBrain *brainTask = TRACKED_NEW plAvTaskBrain(brain);
|
||||
QueueTask(brainTask);
|
||||
climbAvatar->PushBrain(brain);
|
||||
}
|
||||
// ** potentially controversial:
|
||||
// It's fairly easy for a human brain to hit a climb trigger - like when falling off a wall.
|
||||
|
@ -1003,7 +1003,7 @@ void plClothingOutfit::IUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
void plClothingOutfit::WearDefaultClothing()
|
||||
void plClothingOutfit::WearDefaultClothing(hsBool broadcast)
|
||||
{
|
||||
StripAccessories();
|
||||
|
||||
@ -1041,9 +1041,14 @@ void plClothingOutfit::WearDefaultClothing()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (broadcast) {
|
||||
fSynchClients = true;
|
||||
ForceUpdate(true);
|
||||
}
|
||||
}
|
||||
|
||||
void plClothingOutfit::WearDefaultClothingType(UInt32 clothingType)
|
||||
void plClothingOutfit::WearDefaultClothingType(UInt32 clothingType, hsBool broadcast)
|
||||
{
|
||||
plClothingMgr *cMgr = plClothingMgr::GetClothingMgr();
|
||||
hsTArray<plClothingItem *> items;
|
||||
@ -1072,6 +1077,11 @@ void plClothingOutfit::WearDefaultClothingType(UInt32 clothingType)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (broadcast) {
|
||||
fSynchClients = true;
|
||||
ForceUpdate(true);
|
||||
}
|
||||
}
|
||||
|
||||
void plClothingOutfit::WearMaintainerOutfit()
|
||||
|
@ -199,8 +199,8 @@ public:
|
||||
hsBool DirtySynchState(const char* SDLStateName, UInt32 synchFlags);
|
||||
|
||||
void StripAccessories();
|
||||
void WearDefaultClothing();
|
||||
void WearDefaultClothingType(UInt32 clothingType);
|
||||
void WearDefaultClothing(hsBool broadcast = false);
|
||||
void WearDefaultClothingType(UInt32 clothingType, hsBool broadcast = false);
|
||||
void WearMaintainerOutfit();
|
||||
void WearRandomOutfit();
|
||||
void RemoveMaintainerOutfit();
|
||||
|
@ -408,7 +408,7 @@ void plMouseDevice::CreateCursor( char* cursor )
|
||||
fCursor->SetPosition( 0, 0, 0 );
|
||||
IUpdateCursorSize();
|
||||
|
||||
fCursor->SetVisible( true );
|
||||
fCursor->SetVisible(!bCursorHidden);
|
||||
fCursor->SetOpacity( fOpacity );
|
||||
}
|
||||
|
||||
@ -481,8 +481,8 @@ void plMouseDevice::SetCursorY(hsScalar y)
|
||||
|
||||
void plMouseDevice::HideCursor(hsBool override)
|
||||
{
|
||||
if( fInstance->fCursor != nil )
|
||||
fInstance->fCursor->SetVisible( false );
|
||||
if (fInstance && fInstance->fCursor)
|
||||
fInstance->fCursor->SetVisible(false);
|
||||
|
||||
plMouseDevice::bCursorOverride = (override != 0);
|
||||
plMouseDevice::bCursorHidden = true;
|
||||
@ -499,9 +499,11 @@ void plMouseDevice::ShowCursor(hsBool override)
|
||||
plMouseDevice::bCursorHidden = false;
|
||||
plMouseDevice::bCursorOverride = false;
|
||||
|
||||
if( fInstance->fCursor == nil )
|
||||
fInstance->CreateCursor( fInstance->fCursorID );
|
||||
fInstance->fCursor->SetVisible( true );
|
||||
if (fInstance) {
|
||||
if (!fInstance->fCursor)
|
||||
fInstance->CreateCursor(fInstance->fCursorID);
|
||||
fInstance->fCursor->SetVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void plMouseDevice::NewCursor(char* cursor)
|
||||
@ -510,9 +512,6 @@ void plMouseDevice::NewCursor(char* cursor)
|
||||
fInstance->CreateCursor(cursor);
|
||||
fInstance->SetCursorX(fInstance->GetCursorX());
|
||||
fInstance->SetCursorY(fInstance->GetCursorY());
|
||||
|
||||
if (!plMouseDevice::bCursorHidden)
|
||||
fInstance->fCursor->SetVisible( true );
|
||||
}
|
||||
|
||||
void plMouseDevice::SetCursorOpacity( hsScalar opacity )
|
||||
|
@ -253,15 +253,13 @@ void plInputInterfaceMgr::IUpdateCursor( Int32 newCursor )
|
||||
{
|
||||
char* mouseCursorResID;
|
||||
|
||||
|
||||
fCurrentCursor = newCursor;
|
||||
if( fCurrentCursor == plInputInterface::kCursorHidden )
|
||||
if (newCursor == plInputInterface::kCursorHidden) {
|
||||
plMouseDevice::HideCursor();
|
||||
else
|
||||
{
|
||||
plMouseDevice::ShowCursor();
|
||||
} else {
|
||||
if (fCurrentCursor == plInputInterface::kCursorHidden)
|
||||
plMouseDevice::ShowCursor();
|
||||
|
||||
switch( fCurrentCursor )
|
||||
switch(newCursor)
|
||||
{
|
||||
case plInputInterface::kCursorUp: mouseCursorResID = CURSOR_UP; break;
|
||||
case plInputInterface::kCursorLeft: mouseCursorResID = CURSOR_LEFT; break;
|
||||
@ -286,12 +284,12 @@ void plInputInterfaceMgr::IUpdateCursor( Int32 newCursor )
|
||||
case plInputInterface::kCursorHand: mouseCursorResID = CURSOR_HAND; break;
|
||||
case plInputInterface::kCursorUpward: mouseCursorResID = CURSOR_UPWARD; break;
|
||||
default: mouseCursorResID = CURSOR_OPEN; break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
plMouseDevice::NewCursor( mouseCursorResID );
|
||||
plMouseDevice::NewCursor(mouseCursorResID);
|
||||
}
|
||||
|
||||
fCurrentCursor = newCursor;
|
||||
}
|
||||
|
||||
//// IEval ///////////////////////////////////////////////////////////////////
|
||||
|
@ -133,6 +133,10 @@ REGISTER_CREATABLE( plSpawnModMsg );
|
||||
|
||||
REGISTER_CREATABLE( plSpawnRequestMsg );
|
||||
|
||||
#include "plSubtitleMsg.h"
|
||||
|
||||
REGISTER_CREATABLE(plSubtitleMsg);
|
||||
|
||||
#include "plNodeCleanupMsg.h"
|
||||
|
||||
REGISTER_CREATABLE( plNodeCleanupMsg );
|
||||
|
82
Sources/Plasma/PubUtilLib/plMessage/plSubtitleMsg.h
Normal file
82
Sources/Plasma/PubUtilLib/plMessage/plSubtitleMsg.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
|
||||
#ifndef plSubtitleMsg_inc
|
||||
#define plSubtitleMsg_inc
|
||||
|
||||
#include "../pnMessage/plMessage.h"
|
||||
|
||||
/** Show subtitle text for in-game audio */
|
||||
class plSubtitleMsg : public plMessage
|
||||
{
|
||||
protected:
|
||||
std::string fText;
|
||||
std::string fSpeaker;
|
||||
|
||||
public:
|
||||
plSubtitleMsg() : plMessage()
|
||||
{
|
||||
SetBCastFlag(plMessage::kBCastByExactType);
|
||||
}
|
||||
|
||||
plSubtitleMsg(std::string msgText) : plMessage(), fText(std::move(msgText))
|
||||
{
|
||||
SetBCastFlag(plMessage::kBCastByExactType);
|
||||
}
|
||||
|
||||
plSubtitleMsg(std::string msgText, std::string msgSpeaker) : plMessage(), fText(std::move(msgText)),
|
||||
fSpeaker(std::move(msgSpeaker))
|
||||
{
|
||||
SetBCastFlag(plMessage::kBCastByExactType);
|
||||
}
|
||||
|
||||
CLASSNAME_REGISTER(plSubtitleMsg);
|
||||
GETINTERFACE_ANY(plSubtitleMsg, plMessage);
|
||||
|
||||
void Read(hsStream*, hsResMgr*) override { FATAL("no"); }
|
||||
void Write(hsStream*, hsResMgr*) override { FATAL("no"); }
|
||||
|
||||
std::string GetText() const { return fText; }
|
||||
std::string GetSpeaker() const { return fSpeaker; }
|
||||
};
|
||||
|
||||
#endif // plSubtitleMsg_inc
|
@ -217,6 +217,8 @@ bool plResponderModifier::IIsLocalOnlyCmd(plMessage* cmd)
|
||||
return true;
|
||||
if (plCameraMsg::ConvertNoRef(cmd)) // don't want to change our camera
|
||||
return true;
|
||||
if (plSubWorldMsg::ConvertNoRef(cmd)) // don't want to enter a subworld (changes the avatar SDL)
|
||||
return true;
|
||||
|
||||
plSoundMsg *snd = plSoundMsg::ConvertNoRef( cmd );
|
||||
if( snd != nil && snd->Cmd( plSoundMsg::kIsLocalOnly ) )
|
||||
|
@ -211,7 +211,7 @@ void plNCAgeJoiner::Start () {
|
||||
|
||||
plNetClientMgr * nc = plNetClientMgr::GetInstance();
|
||||
nc->SetFlagsBit(plNetClientMgr::kPlayingGame, false);
|
||||
nc->fServerTimeOffset = 0; // reset since we're connecting to a new server
|
||||
nc->ResetServerTimeOffset();
|
||||
nc->fRequiredNumInitialSDLStates = 0;
|
||||
nc->fNumInitialSDLStates = 0;
|
||||
nc->SetFlagsBit(plNetClientApp::kNeedInitialAgeStateCount);
|
||||
|
@ -133,9 +133,7 @@ plNetClientMgr::plNetClientMgr() :
|
||||
// fProgressBar( nil ),
|
||||
fTaskProgBar( nil ),
|
||||
fMsgRecorder(nil),
|
||||
fServerTimeOffset(0),
|
||||
fTimeSamples(0),
|
||||
fLastTimeUpdate(0),
|
||||
fLastLocalTime(),
|
||||
fListenListMode(kListenList_Distance),
|
||||
fAgeSDLObjectKey(nil),
|
||||
fExperimentalLevel(0),
|
||||
@ -480,34 +478,23 @@ void plNetClientMgr::StartLinkInFX()
|
||||
//
|
||||
void plNetClientMgr::UpdateServerTimeOffset(plNetMessage* msg)
|
||||
{
|
||||
if ((hsTimer::GetSysSeconds() - fLastTimeUpdate) > 5)
|
||||
{
|
||||
fLastTimeUpdate = hsTimer::GetSysSeconds();
|
||||
if (!msg->GetHasTimeSent())
|
||||
return;
|
||||
if (msg->GetTimeSent().AtEpoch())
|
||||
return;
|
||||
|
||||
const plUnifiedTime& msgSentUT = msg->GetTimeSent();
|
||||
if (!msgSentUT.AtEpoch())
|
||||
{
|
||||
double diff = plUnifiedTime::GetTimeDifference(msgSentUT, plClientUnifiedTime::GetCurrentTime());
|
||||
double localTime = hsTimer::GetSeconds();
|
||||
if (localTime - fLastLocalTime < 1.0)
|
||||
return;
|
||||
|
||||
if (fServerTimeOffset == 0)
|
||||
{
|
||||
fServerTimeOffset = diff;
|
||||
}
|
||||
else
|
||||
{
|
||||
fServerTimeOffset = fServerTimeOffset + ((diff - fServerTimeOffset) / ++fTimeSamples);
|
||||
}
|
||||
|
||||
DebugMsg("Setting server time offset to %f", fServerTimeOffset);
|
||||
}
|
||||
}
|
||||
fLastServerTime = msg->GetTimeSent();
|
||||
fLastLocalTime = localTime;
|
||||
}
|
||||
|
||||
void plNetClientMgr::ResetServerTimeOffset()
|
||||
{
|
||||
fServerTimeOffset = 0;
|
||||
fTimeSamples = 0;
|
||||
fLastTimeUpdate = 0;
|
||||
fLastServerTime.ToEpoch();
|
||||
fLastLocalTime = 0.0;
|
||||
}
|
||||
|
||||
//
|
||||
@ -515,14 +502,12 @@ void plNetClientMgr::ResetServerTimeOffset()
|
||||
//
|
||||
plUnifiedTime plNetClientMgr::GetServerTime() const
|
||||
{
|
||||
if ( fServerTimeOffset==0 ) // offline mode or before connecting/calibrating to a server
|
||||
if (fLastServerTime.AtEpoch()) {
|
||||
WarningMsg("Someone asked for the server time, but we don't know it yet!");
|
||||
return plUnifiedTime::GetCurrentTime();
|
||||
|
||||
plUnifiedTime serverUT;
|
||||
if (fServerTimeOffset<0)
|
||||
return plUnifiedTime::GetCurrentTime() - plUnifiedTime(fabs(fServerTimeOffset));
|
||||
else
|
||||
return plUnifiedTime::GetCurrentTime() + plUnifiedTime(fServerTimeOffset);
|
||||
}
|
||||
|
||||
return fLastServerTime + plUnifiedTime(hsTimer::GetSeconds() - fLastLocalTime);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -166,9 +166,8 @@ private:
|
||||
std::string fSPDesiredPlayerName; // SP: the player we want to load from vault.
|
||||
|
||||
// server info
|
||||
double fServerTimeOffset; // diff between our unified time and server's unified time
|
||||
UInt32 fTimeSamples;
|
||||
double fLastTimeUpdate;
|
||||
plUnifiedTime fLastServerTime; // Last received time update from the server
|
||||
double fLastLocalTime; // Last monotonic time (in seconds) when the above update was received
|
||||
|
||||
UInt8 fJoinOrder; // returned by the server
|
||||
|
||||
|
@ -504,25 +504,56 @@ bool plNetLinkingMgr::IProcessVaultNotifyMsg(plVaultNotifyMsg* msg)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cVaultLink != nil)
|
||||
{
|
||||
// This is something that Cyan does... >.<
|
||||
// It's very useful though...
|
||||
if (cVaultLink != nil) {
|
||||
// Verify that if the Age vault already exists that it matches the requested
|
||||
// deferred link. If it doesn't exist, well, I hope you're happy with the result.
|
||||
VaultAgeLinkNode accLink(cVaultLink);
|
||||
accLink.CopyTo(cur);
|
||||
if (RelVaultNode* rvnInfo = cVaultLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1))
|
||||
{
|
||||
if (RelVaultNode* rvnInfo = cVaultLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) {
|
||||
plAgeInfoStruct dest;
|
||||
|
||||
VaultAgeInfoNode accInfo(rvnInfo);
|
||||
accInfo.CopyTo(cur->GetAgeInfo());
|
||||
accInfo.CopyTo(&dest);
|
||||
if (!dest.IsEqualTo(fDeferredLink->GetAgeLink()->GetAgeInfo())) {
|
||||
hsLogEntry(
|
||||
plNetClientMgr::GetInstance()->DebugMsg(
|
||||
"Waiting for a deferred link to '%s' but got AgeInfo for '%s' instead.",
|
||||
fDeferredLink->GetAgeLink()->AsStdString().c_str(),
|
||||
dest.AsStdString().c_str()
|
||||
);
|
||||
);
|
||||
|
||||
rvnInfo->DecRef();
|
||||
return false;
|
||||
}
|
||||
|
||||
rvnInfo->DecRef();
|
||||
}
|
||||
|
||||
// If we're still here, that means the links match. Set the vault copy as our current
|
||||
// AgeLink and AgeInfo. Note this mainly copies the AgeInfo.
|
||||
accLink.CopyTo(cur);
|
||||
hsLogEntry(
|
||||
plNetClientMgr::GetInstance()->DebugMsg(
|
||||
"Performing deferred link to '%s'",
|
||||
cur->AsStdString().c_str()
|
||||
);
|
||||
);
|
||||
|
||||
// Steals fDeferredLink
|
||||
IDoLink(fDeferredLink);
|
||||
fDeferredLink = nil;
|
||||
return true;
|
||||
|
||||
cVaultLink->DecRef();
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
hsLogEntry(
|
||||
plNetClientMgr::GetInstance()->ErrorMsg(
|
||||
"Waiting for a deferred link to '%s' but got a garbage link?",
|
||||
fDeferredLink->GetAgeLink()->AsStdString().c_str()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1328,9 +1328,9 @@ void plPXPhysical::GetSyncState(hsPoint3& pos, hsQuat& rot, hsVector3& linV, hsV
|
||||
|
||||
void plPXPhysical::SetSyncState(hsPoint3* pos, hsQuat* rot, hsVector3* linV, hsVector3* angV)
|
||||
{
|
||||
bool initialSync = plNetClientApp::GetInstance()->IsLoadingInitialAgeState() &&
|
||||
plNetClientApp::GetInstance()->GetJoinOrder() == 0;
|
||||
|
||||
bool isLoading = plNetClientApp::GetInstance()->IsLoadingInitialAgeState();
|
||||
bool isFirstIn = plNetClientApp::GetInstance()->GetJoinOrder() == 0;
|
||||
bool initialSync = isLoading && isFirstIn;
|
||||
// If the physical has fallen out of the sim, and this is initial age state, and we're
|
||||
// the first person in, reset it to the original position. (ie, prop the default state
|
||||
// we've got right now)
|
||||
@ -1350,6 +1350,13 @@ void plPXPhysical::SetSyncState(hsPoint3* pos, hsQuat* rot, hsVector3* linV, hsV
|
||||
SetLinearVelocitySim(*linV);
|
||||
if (angV)
|
||||
SetAngularVelocitySim(*angV);
|
||||
// If we're loading the age, then we should ensure the objects stay asleep if they're supposed to be asleep.
|
||||
// NOTE: We should only do this if the objects are not at their initial locations. Otherwise, they might
|
||||
// sleep inside each other and explode or float randomly in midair
|
||||
if (isLoading && GetProperty(plSimulationInterface::kStartInactive) && !fActor->readBodyFlag(NX_BF_KINEMATIC)) {
|
||||
if (!pos && !rot)
|
||||
fActor->putToSleep();
|
||||
}
|
||||
|
||||
SendNewLocation(false, true);
|
||||
}
|
||||
|
@ -59,8 +59,10 @@ plRegistryKeyList::~plRegistryKeyList()
|
||||
for (int i = 0; i < fStaticKeys.size(); i++)
|
||||
{
|
||||
plKeyImp* keyImp = fStaticKeys[i];
|
||||
if (!keyImp->ObjectIsLoaded())
|
||||
if (keyImp && !keyImp->ObjectIsLoaded()) {
|
||||
fStaticKeys[i] = nullptr;
|
||||
delete keyImp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,6 +203,7 @@ void plRegistryPageNode::UnloadKeys()
|
||||
for (; it != fKeyLists.end(); it++)
|
||||
{
|
||||
plRegistryKeyList* keyList = it->second;
|
||||
it->second = nullptr;
|
||||
delete keyList;
|
||||
}
|
||||
fKeyLists.clear();
|
||||
|
@ -219,14 +219,20 @@ void plResManager::IShutdown()
|
||||
// Shut down the registry (finally!)
|
||||
ILockPages();
|
||||
|
||||
PageSet::const_iterator it;
|
||||
for (it = fAllPages.begin(); it != fAllPages.end(); it++)
|
||||
delete *it;
|
||||
fAllPages.clear();
|
||||
// Unload all keys before actually deleting the pages.
|
||||
// When a key's refcount drops to zero, IKeyUnreffed looks up the key's page.
|
||||
// If the page is already deleted at that point, this causes a use after free and potential crash.
|
||||
for (PageSet::const_iterator it = fAllPages.begin(); it != fAllPages.end(); it++) {
|
||||
(*it)->UnloadKeys();
|
||||
}
|
||||
fLoadedPages.clear();
|
||||
fLastFoundPage = nil;
|
||||
for (PageSet::const_iterator it = fAllPages.begin(); it != fAllPages.end(); it++) {
|
||||
delete *it;
|
||||
}
|
||||
fAllPages.clear();
|
||||
|
||||
IUnlockPages();
|
||||
fLastFoundPage = nil;
|
||||
|
||||
// Now, kill off the Dispatcher
|
||||
hsRefCnt_SafeUnRef(fDispatch);
|
||||
@ -536,7 +542,7 @@ inline plKeyImp* IFindKeyLocalized(const plUoid& uoid, plRegistryPageNode* page)
|
||||
const char* objectName = uoid.GetObjectName();
|
||||
|
||||
// If we're running localized, try to find a localized version first
|
||||
if (plLocalization::IsLocalized())
|
||||
if (objectName != nullptr && plLocalization::IsLocalized())
|
||||
{
|
||||
char localName[256];
|
||||
if (plLocalization::GetLocalized(objectName, localName))
|
||||
@ -1299,7 +1305,7 @@ void plResManager::PageInAge(const char *age)
|
||||
|
||||
hsBool plResManager::VerifyPages()
|
||||
{
|
||||
hsTArray<plRegistryPageNode*> invalidPages, newerPages;
|
||||
PageSet invalidPages, newerPages;
|
||||
|
||||
// Step 1: verify major/minor version changes
|
||||
if (plResMgrSettings::Get().GetFilterNewerPageVersions() ||
|
||||
@ -1313,7 +1319,7 @@ hsBool plResManager::VerifyPages()
|
||||
|
||||
if (page->GetPageCondition() == kPageTooNew && plResMgrSettings::Get().GetFilterNewerPageVersions())
|
||||
{
|
||||
newerPages.Append(page);
|
||||
newerPages.insert(page);
|
||||
fAllPages.erase(page);
|
||||
}
|
||||
else if (
|
||||
@ -1321,21 +1327,21 @@ hsBool plResManager::VerifyPages()
|
||||
page->GetPageCondition() == kPageOutOfDate)
|
||||
&& plResMgrSettings::Get().GetFilterOlderPageVersions())
|
||||
{
|
||||
invalidPages.Append(page);
|
||||
invalidPages.insert(page);
|
||||
fAllPages.erase(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle all our invalid pages now
|
||||
if (invalidPages.GetCount() > 0)
|
||||
if (!invalidPages.empty())
|
||||
{
|
||||
if (!IDeleteBadPages(invalidPages, false))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Warn about newer pages
|
||||
if (newerPages.GetCount() > 0)
|
||||
if (!newerPages.empty())
|
||||
{
|
||||
if (!IWarnNewerPages(newerPages))
|
||||
return false;
|
||||
@ -1343,22 +1349,35 @@ hsBool plResManager::VerifyPages()
|
||||
|
||||
// Step 2 of verification: make sure no sequence numbers conflict
|
||||
PageSet::iterator it = fAllPages.begin();
|
||||
for (; it != fAllPages.end(); it++)
|
||||
for (; it != fAllPages.end();)
|
||||
{
|
||||
plRegistryPageNode* page = *it;
|
||||
|
||||
PageSet::iterator itUp = it;
|
||||
itUp++;
|
||||
for (; itUp != fAllPages.end(); itUp++)
|
||||
bool amValid = true;
|
||||
for (; itUp != fAllPages.end();)
|
||||
{
|
||||
plRegistryPageNode* upPage = *itUp;
|
||||
if (page->GetPageInfo().GetLocation() == upPage->GetPageInfo().GetLocation())
|
||||
{
|
||||
invalidPages.Append(upPage);
|
||||
fAllPages.erase(itUp);
|
||||
break;
|
||||
if (page->GetPageInfo().GetLocation() == upPage->GetPageInfo().GetLocation()) {
|
||||
invalidPages.insert(upPage);
|
||||
itUp = fAllPages.erase(itUp);
|
||||
amValid = false;
|
||||
} else {
|
||||
itUp++;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete *all* conflicting pages, not just conflicting - 1.
|
||||
// If we leave in a single conflicting page, a new conflict can trivially
|
||||
// be introduced if the page we leave behind is defunct and a new version
|
||||
// is later downloaded from the FileSrv.
|
||||
if (!amValid) {
|
||||
invalidPages.insert(page);
|
||||
it = fAllPages.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Redo our loaded pages list, since Verify() might force the page's keys to load or unload
|
||||
@ -1374,7 +1393,7 @@ hsBool plResManager::VerifyPages()
|
||||
}
|
||||
|
||||
// Handle all our conflicting pages now
|
||||
if (invalidPages.GetCount() > 0)
|
||||
if (!invalidPages.empty())
|
||||
return IDeleteBadPages(invalidPages, true);
|
||||
|
||||
return true;
|
||||
@ -1384,9 +1403,10 @@ hsBool plResManager::VerifyPages()
|
||||
// Given an array of pages that are invalid (major version out-of-date or
|
||||
// whatnot), asks the user what we should do about them.
|
||||
|
||||
static void ICatPageNames(hsTArray<plRegistryPageNode*>& pages, char* buf, int bufSize)
|
||||
static void ICatPageNames(const std::set<plRegistryPageNode*>& pages, char* buf, int bufSize)
|
||||
{
|
||||
for (int i = 0; i < pages.GetCount(); i++)
|
||||
int i = 0;
|
||||
for (auto it = pages.cbegin(); it != pages.cend(); ++it, ++i)
|
||||
{
|
||||
if (i >= 25)
|
||||
{
|
||||
@ -1394,7 +1414,7 @@ static void ICatPageNames(hsTArray<plRegistryPageNode*>& pages, char* buf, int b
|
||||
break;
|
||||
}
|
||||
|
||||
const char* pagePath = pages[i]->GetPagePath();
|
||||
const char* pagePath = (*it)->GetPagePath();
|
||||
const char* pageFile = plFileUtils::GetFileName(pagePath);
|
||||
|
||||
if (strlen(buf) + strlen(pageFile) > bufSize - 5)
|
||||
@ -1408,7 +1428,7 @@ static void ICatPageNames(hsTArray<plRegistryPageNode*>& pages, char* buf, int b
|
||||
}
|
||||
}
|
||||
|
||||
hsBool plResManager::IDeleteBadPages(hsTArray<plRegistryPageNode*>& invalidPages, hsBool conflictingSeqNums)
|
||||
hsBool plResManager::IDeleteBadPages(PageSet& invalidPages, hsBool conflictingSeqNums)
|
||||
{
|
||||
#ifndef PLASMA_EXTERNAL_RELEASE
|
||||
if (!hsMessageBox_SuppressPrompts)
|
||||
@ -1430,12 +1450,12 @@ hsBool plResManager::IDeleteBadPages(hsTArray<plRegistryPageNode*>& invalidPages
|
||||
#endif // PLASMA_EXTERNAL_RELEASE
|
||||
|
||||
// Delete 'em
|
||||
for (int i = 0; i < invalidPages.GetCount(); i++)
|
||||
for (PageSet::iterator it = invalidPages.begin(); it != invalidPages.end(); ++it)
|
||||
{
|
||||
invalidPages[i]->DeleteSource();
|
||||
delete invalidPages[i];
|
||||
(*it)->DeleteSource();
|
||||
delete *it;
|
||||
}
|
||||
invalidPages.Reset();
|
||||
invalidPages.clear();
|
||||
|
||||
fLastFoundPage = nil;
|
||||
|
||||
@ -1447,7 +1467,7 @@ hsBool plResManager::IDeleteBadPages(hsTArray<plRegistryPageNode*>& invalidPages
|
||||
// than the "current" one), warns the user about them but does nothing to
|
||||
// them.
|
||||
|
||||
hsBool plResManager::IWarnNewerPages(hsTArray<plRegistryPageNode*> &newerPages)
|
||||
hsBool plResManager::IWarnNewerPages(PageSet &newerPages)
|
||||
{
|
||||
#ifndef PLASMA_EXTERNAL_RELEASE
|
||||
if (!hsMessageBox_SuppressPrompts)
|
||||
@ -1465,9 +1485,9 @@ hsBool plResManager::IWarnNewerPages(hsTArray<plRegistryPageNode*> &newerPages)
|
||||
|
||||
|
||||
// Not deleting the files, just delete them from memory
|
||||
for (int i = 0; i < newerPages.GetCount(); i++)
|
||||
delete newerPages[i];
|
||||
newerPages.Reset();
|
||||
for (PageSet::iterator it = newerPages.begin(); it != newerPages.end(); ++it)
|
||||
delete *it;
|
||||
newerPages.clear();
|
||||
|
||||
fLastFoundPage = nil;
|
||||
|
||||
|
@ -61,6 +61,9 @@ typedef void(*plProgressProc)(plKey key);
|
||||
|
||||
class plResManager : public hsResMgr
|
||||
{
|
||||
protected:
|
||||
typedef std::set<plRegistryPageNode*> PageSet;
|
||||
|
||||
public:
|
||||
plResManager();
|
||||
virtual ~plResManager();
|
||||
@ -194,8 +197,8 @@ protected:
|
||||
|
||||
void IUnloadPageKeys(plRegistryPageNode* pageNode, hsBool dontClear = false);
|
||||
|
||||
hsBool IDeleteBadPages(hsTArray<plRegistryPageNode*>& invalidPages, hsBool conflictingSeqNums);
|
||||
hsBool IWarnNewerPages(hsTArray<plRegistryPageNode*>& newerPages);
|
||||
hsBool IDeleteBadPages(PageSet& invalidPages, hsBool conflictingSeqNums);
|
||||
hsBool IWarnNewerPages(PageSet& newerPages);
|
||||
|
||||
void ILockPages();
|
||||
void IUnlockPages();
|
||||
@ -233,7 +236,6 @@ protected:
|
||||
UInt8 fPageListLock; // Number of locks on the page lists. If it's greater than zero, they can't be modified
|
||||
hsBool fPagesNeedCleanup; // True if something modified the page lists while they were locked.
|
||||
|
||||
typedef std::set<plRegistryPageNode*> PageSet;
|
||||
PageSet fAllPages; // All the pages, loaded or not
|
||||
PageSet fLoadedPages; // Just the loaded pages
|
||||
|
||||
|
Reference in New Issue
Block a user