Skip to content

Commit

Permalink
Merge pull request UOX3DevTeam#163 from UOX3DevTeam/misc_updates_3112
Browse files Browse the repository at this point in the history
Misc updates
  • Loading branch information
Xoduz authored Jan 2, 2023
2 parents b3ad45c + 61a2da3 commit 8e82bb6
Show file tree
Hide file tree
Showing 16 changed files with 122 additions and 17 deletions.
1 change: 1 addition & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11098,6 +11098,7 @@ <h3>How it Got Started</h3>
<div class="spoiler">
<p><span class="hl">Grimson</span><br> Programmer, 07/06/2005, 0.98.3-0</p>
<p><span class="hl">ShadowBranch</span><br> Daniel Moree, Programmer, 11/06/2005, 0.98-3.0</p>
<p><span class="hl">Sydius</span><br> Chris Ogden, Programmer, 04/08/2005, 0.98-3.0</p>
<p><span class="hl">lingo</span><br> Lingo Chen, Programmer, 05/08/2005, 0.98-3.0e</p>
</div>

Expand Down
15 changes: 14 additions & 1 deletion docs/jsdocs.html
Original file line number Diff line number Diff line change
Expand Up @@ -7067,15 +7067,20 @@ <h4>January 9th, 2022</h4>
<div class="settingsDiv">
<p><span class="hl">Prototype</span></p>
<p><em>SpawnRegion GetSpawnRegion( spawnRegionNum );</em></p>
<p><em>SpawnRegion GetSpawnRegion( x, y, worldNum, instanceID );</em></p>
</div>
<div class="settingsDiv">
<p><span class="hl">Purpose</span></p>
<p>Returns SpawnRegion object for specified spawnRegionNum.</p>
<p>Returns SpawnRegion object for specified spawnRegionNum, or for specified set of coordinates</p>
</div>
<div class="settingsDiv">
<p><span class="hl">Example of usage</span></p>
<pre><code class="language-javascript">// Returns SpawnRegion object for spawn region #1 from spawns.dfn
var spawnRegion = GetSpawnRegion( 1 );
pUser.TextMessage( "The name of this SpawnRegion is: " + spawnRegion.name );

// Returns SpawnRegion object for spawn region at specified coordinates:
var spawnRegion = GetSpawnRegion( pUser.x, pUser.y, pUser.worldnumber, pUser.instanceID );
pUser.TextMessage( "The name of this SpawnRegion is: " + spawnRegion.name );</code></pre>
</div>
</div>
Expand Down Expand Up @@ -14211,6 +14216,10 @@ <h3>Text Types</h3>
<p><span class="hl">.name</span> <strong>(text, <em>max 127 characters</em>)</strong><br>
Get/Set name of character</p>
</div>
<div class="settingsDiv">
<p><span class="hl">.origin</span> <strong>(text, <em>max 127 characters</em>)</strong><br>
Get/Set origin (expansion, era) of character</p>
</div>
<div class="settingsDiv">
<p><span class="hl">.sectionID</span> <strong>(text, <em>max 127 characters</em>)</strong><br>
Section ID from DFNs that object originated from</p>
Expand Down Expand Up @@ -14869,6 +14878,10 @@ <h3>Text Types</h3>
<p><span class="hl">.name2</span> <strong>(text, <em>max 127 characters</em>)</strong><br>
Get/Set secondary name of item - used in relation to magical items whose names get revealed upon successful use of Item Identification</p>
</div>
<div class="settingsDiv">
<p><span class="hl">.origin</span> <strong>(text, <em>max 127 characters</em>)</strong><br>
Get/Set origin (expansion, era) of item</p>
</div>
<div class="settingsDiv">
<p><span class="hl">.sectionID</span> <strong>(text, <em>max 127 characters</em>)</strong><br>
Section ID from DFNs that object originated from</p>
Expand Down
12 changes: 12 additions & 0 deletions source/Changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
31/12/2022 - Xuri
Extended GetSpawnRegion JS Function to fetch spawn region reference based on a set of coordinates. Supported syntax are now:
GetSpawnRegion( spawnRegionID )
GetSpawnRegion( x, y, worldNum, instanceID )
Added new DFN tags for Items and NPC:
SECTIONID // Override which [sectionID] is stored for an object
ORIGIN // Store reference to which expansion/era of UO an item/NPC originated in
Added new Item/Character JS property:
.origin // Get reference to expansion/era of UO an item/NPC originated in
Fixed an issue with IsInBuilding JS Function which could only handled a limited subset of instance ID values
Corrected name of tag used to attach scripts to ore types - from SCRIPTID to SCRIPT

24/12/2022 - Xuri, punt, ldilley (0.99.6-RC2)
CMake is now default build system on Linux/FreeBSD, and an alternative for Windows/macOS-users instead of VS/XCode, though the old GCC/Make build process is still intact (for now)
Resource files (uox3.rc and uox3.ico) have been moved from UOX3/source to UOX3/assets
Expand Down
51 changes: 41 additions & 10 deletions source/SEFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3420,20 +3420,28 @@ JSBool SE_GetTownRegion( JSContext *cx, [[maybe_unused]] JSObject *obj, uintN ar
//o------------------------------------------------------------------------------------------------o
JSBool SE_GetSpawnRegion( JSContext *cx, [[maybe_unused]] JSObject *obj, uintN argc, jsval *argv, jsval *rval )
{
if( argc != 1 )
if( argc != 1 && argc != 4 )
{
DoSEErrorMessage( "GetSpawnRegion: Invalid number of parameters (1)" );
DoSEErrorMessage( "GetSpawnRegion: Invalid number of parameters (1 - spawnRegionID, or 4 - x, y, world and instanceID)" );
return JS_FALSE;
}

UI16 spawnRegNum = static_cast<UI16>( JSVAL_TO_INT( argv[0] ));
if( cwmWorldState->spawnRegions.find( spawnRegNum ) != cwmWorldState->spawnRegions.end() )
if( argc == 1 )
{
CSpawnRegion *spawnReg = cwmWorldState->spawnRegions[spawnRegNum];
if( spawnReg != nullptr )
// Assume spawn region number was provided
UI16 spawnRegNum = static_cast<UI16>( JSVAL_TO_INT( argv[0] ));
if( cwmWorldState->spawnRegions.find( spawnRegNum ) != cwmWorldState->spawnRegions.end() )
{
JSObject *myObj = JSEngine->AcquireObject( IUE_SPAWNREGION, spawnReg, JSEngine->FindActiveRuntime( JS_GetRuntime( cx )));
*rval = OBJECT_TO_JSVAL( myObj );
CSpawnRegion *spawnReg = cwmWorldState->spawnRegions[spawnRegNum];
if( spawnReg != nullptr )
{
JSObject *myObj = JSEngine->AcquireObject( IUE_SPAWNREGION, spawnReg, JSEngine->FindActiveRuntime( JS_GetRuntime( cx )));
*rval = OBJECT_TO_JSVAL( myObj );
}
else
{
*rval = JSVAL_NULL;
}
}
else
{
Expand All @@ -3442,8 +3450,31 @@ JSBool SE_GetSpawnRegion( JSContext *cx, [[maybe_unused]] JSObject *obj, uintN a
}
else
{
*rval = JSVAL_NULL;
// Assume coordinates were provided
UI16 x = static_cast<UI16>( JSVAL_TO_INT( argv[0] ));
UI16 y = static_cast<UI16>( JSVAL_TO_INT( argv[1] ));
UI08 worldNum = static_cast<UI08>( JSVAL_TO_INT( argv[2] ));
UI16 instanceID = static_cast<UI16>( JSVAL_TO_INT( argv[3] ));

// Iterate over each spawn region to find the right one
auto iter = std::find_if( cwmWorldState->spawnRegions.begin(), cwmWorldState->spawnRegions.end(), [&x, &y, &worldNum, &instanceID, &cx, &rval]( std::pair<UI16, CSpawnRegion*> entry )
{
if( entry.second && x >= entry.second->GetX1() && x <= entry.second->GetX2() && y >= entry.second->GetY1()
&& y <= entry.second->GetY2() && entry.second->GetInstanceId() == instanceID && entry.second->WorldNumber() == worldNum )
{
JSObject *myObj = JSEngine->AcquireObject( IUE_SPAWNREGION, entry.second, JSEngine->FindActiveRuntime( JS_GetRuntime( cx )));
*rval = OBJECT_TO_JSVAL( myObj );
return true;
}
return false;
});

if( iter == cwmWorldState->spawnRegions.end() )
{
*rval = JSVAL_NULL;
}
}

return JS_TRUE;
}

Expand Down Expand Up @@ -3504,7 +3535,7 @@ JSBool SE_IsInBuilding( [[maybe_unused]] JSContext *cx, [[maybe_unused]] JSObjec
SI16 y = static_cast<SI16>( JSVAL_TO_INT( argv[1] ));
SI08 z = static_cast<SI08>( JSVAL_TO_INT( argv[2] ));
UI08 worldNum = static_cast<UI08>( JSVAL_TO_INT( argv[3] ));
UI08 instanceId = static_cast<UI08>( JSVAL_TO_INT( argv[4] ));
UI16 instanceId = static_cast<UI16>( JSVAL_TO_INT( argv[4] ));
bool checkHeight = ( JSVAL_TO_BOOLEAN( argv[5] ) == JS_TRUE );
bool isInBuilding = Map->InBuilding( x, y, z, worldNum, instanceId );
if( !isInBuilding )
Expand Down
2 changes: 1 addition & 1 deletion source/UOXJSMethods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3674,7 +3674,7 @@ JSBool CBase_UseResource( JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
sectionId = JS_GetStringBytes( JS_ValueToString( cx, argv[4] ));
}

bool colorCheck = ( itemColour != -1 );
bool colorCheck = ( itemColour != -1 ? true : false );
bool moreCheck = ( moreVal != -1 ? true : false );

UI32 retVal = 0;
Expand Down
2 changes: 2 additions & 0 deletions source/UOXJSPropertyEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ enum CC_Properties
CCP_LOYALTY,
CCP_LOYALTYRATE,
CCP_SHOULDSAVE,
CCP_ORIGIN,
CCP_PARTYLOOTABLE,
CCP_PARTY,
CCP_MULTI,
Expand Down Expand Up @@ -512,6 +513,7 @@ enum CI_Properties
CIP_BASERANGE,
CIP_REGION,
CIP_SPAWNSERIAL,
CIP_ORIGIN,
CIP_ISITEMHELD,
CIP_SECTIONID,

Expand Down
5 changes: 5 additions & 0 deletions source/UOXJSPropertyFuncs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,10 @@ JSBool CItemProps_getProperty( JSContext *cx, JSObject *obj, jsval id, jsval *vp
break;
}
case CIP_SPAWNSERIAL: *vp = INT_TO_JSVAL( gPriv->GetSpawn() ); break;
case CIP_ORIGIN:
tString = JS_NewStringCopyZ( cx, gPriv->GetOrigin().c_str() );
*vp = STRING_TO_JSVAL( tString );
break;
case CIP_ISITEMHELD: *vp = BOOLEAN_TO_JSVAL( gPriv->IsHeldOnCursor() ); break;

// The following entries are specifically for CSpawnItem objects
Expand Down Expand Up @@ -1269,6 +1273,7 @@ JSBool CItemProps_setProperty( JSContext *cx, JSObject *obj, jsval id, jsval *vp
case CIP_MAXRANGE: gPriv->SetMaxRange( static_cast<UI08>( encaps.toInt() )); break;
case CIP_BASERANGE: gPriv->SetBaseRange( static_cast<UI08>( encaps.toInt() )); break;
case CIP_REGION: gPriv->SetRegion( static_cast<UI16>( encaps.toInt() )); break;
case CIP_ORIGIN: gPriv->SetOrigin( encaps.toString() ); break;
case CIP_ISITEMHELD: gPriv->SetHeldOnCursor( encaps.toBool() ); break;

// The following entries are specifically for CSpawnItem objects
Expand Down
2 changes: 2 additions & 0 deletions source/UOXJSPropertySpecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ inline JSPropertySpec CCharacterProps[] =
{ "loyalty", CCP_LOYALTY, JSPROP_ENUMANDPERM, nullptr, nullptr },
{ "loyaltyRate", CCP_LOYALTYRATE, JSPROP_ENUMANDPERM, nullptr, nullptr },
{ "shouldSave", CCP_SHOULDSAVE, JSPROP_ENUMANDPERM, nullptr, nullptr },
{ "origin", CCP_ORIGIN, JSPROP_ENUMANDPERM },
{ "partyLootable", CCP_PARTYLOOTABLE, JSPROP_ENUMANDPERM, nullptr, nullptr },
{ "party", CCP_PARTY, JSPROP_ENUMPERMRO, nullptr, nullptr },
{ "multi", CCP_MULTI, JSPROP_ENUMANDPERM, nullptr, nullptr },
Expand Down Expand Up @@ -523,6 +524,7 @@ inline JSPropertySpec CItemProps[] =
{ "baseRange", CIP_BASERANGE, JSPROP_ENUMANDPERM, nullptr, nullptr },
{ "region", CIP_REGION, JSPROP_ENUMPERMRO, nullptr, nullptr },
{ "spawnSerial", CIP_SPAWNSERIAL, JSPROP_ENUMPERMRO, nullptr, nullptr },
{ "origin", CIP_ORIGIN, JSPROP_ENUMANDPERM },
{ "isItemHeld", CIP_ISITEMHELD, JSPROP_ENUMANDPERM, nullptr, nullptr },

// The Following vars are specific to CSpawnItem objects
Expand Down
5 changes: 5 additions & 0 deletions source/cBaseObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class CBaseObject
SI16 karma;
SI16 kills;
UI16 subRegion;
std::string origin; // Stores expansion item originates from

void RemoveFromMulti( bool fireTrigger = true );
void AddToMulti( bool fireTrigger = true );
Expand Down Expand Up @@ -126,6 +127,10 @@ class CBaseObject

void SetTitle( std::string newtitle );
std::string GetTitle( void ) const;

void SetOrigin( std::string newOrigin );
std::string GetOrigin( void ) const;

virtual void SetMana( SI16 mn );
SI16 GetMana( void ) const;
virtual void SetStamina( SI16 stam );
Expand Down
23 changes: 22 additions & 1 deletion source/cBaseobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,7 @@ bool CBaseObject::DumpBody( std::ofstream &outStream ) const
std::string myLocation = "Location=" + std::to_string( x ) + "," + std::to_string( y ) + "," +std::to_string( z ) + "," + std::to_string( worldNumber ) + "," + std::to_string( instanceId ) + newLine;
outStream << myLocation;
outStream << "Title=" << title << newLine;
outStream << "Origin=" << origin << newLine;

//=========== BUG (= For Characters the dex+str+int malis get saved and get rebuilt on next serverstartup = increasing malis)
temp_st2 = st2;
Expand Down Expand Up @@ -1416,6 +1417,21 @@ void CBaseObject::SetTitle( std::string newtitle )
title = newtitle.substr( 0, MAX_TITLE - 1 );
}

//o------------------------------------------------------------------------------------------------o
//| Function - CBaseObject::GetOrigin()
//| CBaseObject::SetOrigin()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets the object's origin
//o------------------------------------------------------------------------------------------------o
std::string CBaseObject::GetOrigin( void ) const
{
return origin;
}
void CBaseObject::SetOrigin( std::string newOrigin )
{
origin = newOrigin.substr( 0, MAX_ORIGIN );
}

//o------------------------------------------------------------------------------------------------o
//| Function - CBaseObject::GetScriptTriggers()
//o------------------------------------------------------------------------------------------------o
Expand Down Expand Up @@ -1896,7 +1912,11 @@ bool CBaseObject::HandleLine( std::string &UTag, std::string &data )
}
break;
case 'O':
if( UTag == "OWNERID" )
if( UTag == "ORIGIN" )
{
origin = data.substr( 0, MAX_ORIGIN - 1 );
}
else if( UTag == "OWNERID" )
{
owner = oldstrutil::value<UI32>( data );
}
Expand Down Expand Up @@ -2452,6 +2472,7 @@ void CBaseObject::CopyData( CBaseObject *target )
{
target->SetSectionId( GetSectionId() );
target->SetTitle( GetTitle() );
target->SetOrigin( GetOrigin() );
target->SetRace( GetRace() );
target->SetName( GetName() );
target->SetStrength( GetStrength() );
Expand Down
2 changes: 1 addition & 1 deletion source/cScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ static JSFunctionSpec my_functions[] =
{ "Moon", SE_Moon, 2, 0, 0 },

{ "GetTownRegion", SE_GetTownRegion, 1, 0, 0 },
{ "GetSpawnRegion", SE_GetSpawnRegion, 1, 0, 0 },
{ "GetSpawnRegion", SE_GetSpawnRegion, 4, 0, 0 },
{ "GetSpawnRegionCount", SE_GetSpawnRegionCount, 0, 0, 0 },


Expand Down
9 changes: 7 additions & 2 deletions source/items.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ auto ApplyItemSection( CItem *applyTo, CScriptSection *toApply, std::string sect
case DFNTAG_NAME2: applyTo->SetName2( cdata ); break;
case DFNTAG_NEWBIE: applyTo->SetNewbie( true ); break;
case DFNTAG_OFFSPELL: applyTo->SetOffSpell( static_cast<SI08>( ndata )); break;
case DFNTAG_ORIGIN: applyTo->SetOrigin( cdata ); break;
case DFNTAG_POISONDAMAGE: applyTo->SetWeatherDamage( POISON, ndata != 0 ); break;
case DFNTAG_POISONED: applyTo->SetPoisoned( static_cast<UI08>( ndata )); break;
case DFNTAG_PILEABLE: applyTo->SetPileable( ndata != 0 ); break;
Expand Down Expand Up @@ -507,6 +508,7 @@ auto ApplyItemSection( CItem *applyTo, CScriptSection *toApply, std::string sect
break;
}
case DFNTAG_RAIN: applyTo->SetWeatherDamage( RAIN, ndata != 0 ); break;
case DFNTAG_SECTIONID: applyTo->SetSectionId( cdata ); break;
case DFNTAG_SK_MADE: applyTo->SetMadeWith( static_cast<SI08>( ndata )); break;
case DFNTAG_SPD: applyTo->SetSpeed( static_cast<UI08>( ndata )); break;
case DFNTAG_STRENGTH: applyTo->SetStrength( static_cast<SI16>( ndata )); break;
Expand Down Expand Up @@ -1195,8 +1197,11 @@ CItem * cItem::CreateBaseScriptItem( CItem *mCont, std::string ourItem, const UI
iCreated->SetAmount( iAmount );
}

// Keep reference to DFN sectionId item was created from
iCreated->SetSectionId( ourItem );
// Keep reference to DFN sectionId item was created from (if it has not been set already via special DFN tag SECTIONID)
if( iCreated->GetSectionId() == "" )
{
iCreated->SetSectionId( ourItem );
}
}

return iCreated;
Expand Down
2 changes: 2 additions & 0 deletions source/npcs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,7 @@ auto CCharStuff::ApplyNpcSection( CChar *applyTo, CScriptSection *NpcCreation, s
applyTo->SetCanTrain( false );
}
break;
case DFNTAG_ORIGIN: applyTo->SetOrigin( cdata ); break;
case DFNTAG_POISONSTRENGTH: applyTo->SetPoisonStrength( static_cast<UI08>( ndata )); break;
case DFNTAG_PRIV:
if( !isGate )
Expand Down Expand Up @@ -1552,6 +1553,7 @@ auto CCharStuff::ApplyNpcSection( CChar *applyTo, CScriptSection *NpcCreation, s
case DFNTAG_RUNNINGSPEEDMOUNTED:
applyTo->SetMountedRunningSpeed( std::stof( cdata ));
break;
case DFNTAG_SECTIONID: applyTo->SetSectionId( cdata ); break;
case DFNTAG_SKIN: applyTo->SetSkin( static_cast<UI16>( ndata )); break;
case DFNTAG_SHOPKEEPER:
if( !isGate )
Expand Down
2 changes: 1 addition & 1 deletion source/skills.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2123,7 +2123,7 @@ bool CSkills::LoadMiningData( void )
}
break;
case 'S':
if( UTag == "SCRIPTID" )
if( UTag == "SCRIPT" )
{
toAdd.scriptID = static_cast<UI16>( std::stoul( data, nullptr, 0 ));
}
Expand Down
4 changes: 4 additions & 0 deletions source/ssection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ const UI08 dfnDataTypes[DFNTAG_COUNTOFTAGS] =
DFN_UPPERSTRING, // DFNTAG_NPCLIST,
DFN_NUMERIC, // DFNTAG_NPCWANDER,
DFN_NUMERIC, // DFNTAG_OFFSPELL,
DFN_STRING, // DFNTAG_ORIGIN,
DFN_STRING, // DFNTAG_PACKITEM,
DFN_DOUBLENUMERIC, // DFNTAG_PARRYING,
DFN_DOUBLENUMERIC, // DFNTAG_PEACEMAKING,
Expand Down Expand Up @@ -194,6 +195,7 @@ const UI08 dfnDataTypes[DFNTAG_COUNTOFTAGS] =
DFN_NODATA, // DFNTAG_RUNS,
DFN_NUMERIC, // DFNTAG_SAYCOLOUR,
DFN_NUMERIC, // DFNTAG_SCRIPT,
DFN_STRING, // DFNTAG_SECTIONID,
DFN_STRING, // DFNTAG_SELLITEM,
DFN_STRING, // DFNTAG_SHOPITEM,
DFN_NODATA, // DFNTAG_SHOPKEEPER,
Expand Down Expand Up @@ -419,6 +421,7 @@ const std::map<std::string, DFNTAGS> strToDFNTag
{"NPCLIST"s, DFNTAG_NPCLIST},
{"NPCWANDER"s, DFNTAG_NPCWANDER},
{"OFFSPELL"s, DFNTAG_OFFSPELL},
{"ORIGIN"s, DFNTAG_ORIGIN},
{"PACKITEM"s, DFNTAG_PACKITEM},
{"PARRYING"s, DFNTAG_PARRYING},
{"PEACEMAKING"s, DFNTAG_PEACEMAKING},
Expand Down Expand Up @@ -447,6 +450,7 @@ const std::map<std::string, DFNTAGS> strToDFNTag
{"SAYCOLOR"s, DFNTAG_SAYCOLOUR},
{"SAYCOLOUR"s, DFNTAG_SAYCOLOUR},
{"SCRIPT"s, DFNTAG_SCRIPT},
{"SECTIONID"s, DFNTAG_SECTIONID},
{"SELLITEM"s, DFNTAG_SELLITEM},
{"SHOPITEM"s, DFNTAG_SHOPITEM},
{"SHOPKEEPER"s, DFNTAG_SHOPKEEPER},
Expand Down
2 changes: 2 additions & 0 deletions source/ssection.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ enum DFNTAGS
DFNTAG_NPCLIST,
DFNTAG_NPCWANDER,
DFNTAG_OFFSPELL,
DFNTAG_ORIGIN,
DFNTAG_PACKITEM,
DFNTAG_PARRYING,
DFNTAG_PEACEMAKING,
Expand Down Expand Up @@ -201,6 +202,7 @@ enum DFNTAGS
DFNTAG_RUNS,
DFNTAG_SAYCOLOUR,
DFNTAG_SCRIPT,
DFNTAG_SECTIONID,
DFNTAG_SELLITEM,
DFNTAG_SHOPITEM,
DFNTAG_SHOPKEEPER,
Expand Down

0 comments on commit 8e82bb6

Please sign in to comment.