I’m excited to share a brand-new system for UOX3 that brings player-owned ranches to life! This system allows players to claim, manage, and fence off their own ranching area, directly in-game.
Ranch Stone
Acts as the heart of your ranch.
Displays ranch details such as owner, size, and area coverage.
Ranch size is upgradeable, paving the way for future expansions.
Custom Fencing System
Players can craft or acquire a Fencing Hammer.
Use the hammer to “set” fence pieces (posts, corners, straights) into the ground.
Fence pieces are validated to ensure they are within your ranch boundary before becoming immovable.
Ownership & Range Checks
Only the ranch owner (or co-owners, if added later) can place fences tied to their stone.
Proper range validation ensures you can’t place or secure fences outside of your ranch’s radius.
Prevents abuse by checking that players are within their stone’s defined area, not just a fixed tile distance.
Upgradeable System
Designed with future expansions in mind:
Larger ranch sizes.
Additional fence types and visuals.
Possible add-ons like gates, barns, or animal handling features.
🛠 Technical Notes:
Makes use of UOX3’s CalcMultiFromSer and FindMulti functions to ensure ranches integrate properly with multi-structures.
Ranch Stones store their size/area data as tags for easy upgrades and future persistence.
Fences become static (non-movable) once hammered in within valid ranch boundaries.
Built entirely in UOX3’s SpiderMonkey 1.8.5 JS engine for easy modification and shard customization.
This system provides a great foundation for roleplay, farming, and animal management features while keeping expansion possibilities wide open. Perfect for shards looking to add depth to housing and land ownership beyond standard homes.
[ranchdeed]
{
get=base_item
name=ranchdeed
id=0x14F0
script=19602
}
[ranchstone]
{
get=base_item
name=ranchstone
id=0xED6
movable=0
script=19602
decay=0
}
[ranchupgrade]
{
get=base_item
id=0x14F0
name=Ranch Size Upgrade
script=19602
}
[Fence]
{
get=base_fence
name=wooden fence
id=0x088A
weight=100
}
[Fencetwo]
{
get=base_fence
name=wooden fence
id=0x088B
weight=100
}
[FenceCorner]
{
get=base_fence
name=wooden fence
id=0x0862
weight=100
}
[FencePost]
{
get=base_fence
name=wooden fence
id=0x085F
weight=100
}
[fencinghammer]
{
get=0x0fb4_lbr
name=fencing hammer
weight=900
value=32 16
damage=12 38
hp=31 60
spd=15
str=30
script=19603
}
{
get=base_item
name=ranchdeed
id=0x14F0
script=19602
}
[ranchstone]
{
get=base_item
name=ranchstone
id=0xED6
movable=0
script=19602
decay=0
}
[ranchupgrade]
{
get=base_item
id=0x14F0
name=Ranch Size Upgrade
script=19602
}
[Fence]
{
get=base_fence
name=wooden fence
id=0x088A
weight=100
}
[Fencetwo]
{
get=base_fence
name=wooden fence
id=0x088B
weight=100
}
[FenceCorner]
{
get=base_fence
name=wooden fence
id=0x0862
weight=100
}
[FencePost]
{
get=base_fence
name=wooden fence
id=0x085F
weight=100
}
[fencinghammer]
{
get=0x0fb4_lbr
name=fencing hammer
weight=900
value=32 16
damage=12 38
hp=31 60
spd=15
str=30
script=19603
}
// ============================================================================
// Fencing Hammer (UOX3 - SpiderMonkey 1.8.5)
// ScriptID: 19603
// File: js/custom/fencinghammer.js
// ============================================================================
var FENCE_TAG = "ranchStoneSerial"; // set on fence when “hammered in”
var STONE_TAG = "ranchStoneSerial"; // stored on hammer to bind it to a stone
var RANGE_TO_STONE_USE = 10; // working range for targeting
var RANGE_RENAME = 5; // “in pack & near stone” quick rename distance
// --- Add near the top with your other constants ---
var FENCE_SECTIONS = {
"Fence": true,
"Fencetwo": true,
"FenceCorner": true,
"FencePost": true
};
function GetRanchSize( iStone )
{
var v = parseInt( iStone.GetTag( "ranchSize" ), 10 );
if( isNaN( v ) || v <= 0 )
v = SIZE_DEFAULT || 10; // fallback if constants not shared here
return v;
}
// Recognize a valid fence/gate item by sectionID
function IsWhitelistedFence( it )
{
return ( it && it.isItem && !!FENCE_SECTIONS[ it.sectionID ] );
}
function GetBoundStoneFromHammer( hammer )
{
if( !ValidateObject( hammer )) return null;
var ser = parseInt( hammer.GetTag( STONE_TAG ), 10 );
if( !ser || ser <= 0 ) return null;
// Try to locate the stone by serial
var stone = CalcItemFromSer ? CalcItemFromSer( ser ) : null;
if( ValidateObject( stone )) return stone;
// Fallback: if not resolvable by serial, at least ensure player is in same multi as stored coords
return null;
}
function IsOwnerNearStone( pChar, stone, maxRange )
{
if( !ValidateObject( pChar ) || !ValidateObject( stone )) return false;
if( pChar.serial != parseInt( stone.GetTag( "ownerSerial" ), 10 )) return false;
return stone.InRange( pChar, maxRange|0 );
}
// Optional: require same house (multi) as stone
function IsSameHouseAsStone( who, stone )
{
var mStone = FindMulti( stone );
var mUser = FindMulti( who );
if( !mStone || !mUser ) return false;
// Basic equality (many shards just compare pointers or serials if exposed)
return ( mStone.serial && mUser.serial && ( mStone.serial == mUser.serial ));
}
function IsPlayerInsideRanch( pUser, stone )
{
if( !ValidateObject(pUser) || !ValidateObject(stone) )
return false;
var radius = TriggerEvent( 19602, "GetRanchSize", stone );
return pUser.InRange(stone, radius);
}
// --- Main use handler ---
function onUseChecked( pUser, iUsed )
{
if( !iUsed || !iUsed.isItem ) return false;
// Only respond to fencing hammer section
if( iUsed.sectionID != "fencinghammer" )
return false;
var socket = pUser.socket;
var stone = GetBoundStoneFromHammer( iUsed );
// If hammer is in pack and near the bound stone, allow quick rename via stone
if( iUsed.container && iUsed.container.isItem && iUsed.container == pUser.pack )
{
if( ValidateObject( stone ) && IsOwnerNearStone( pUser, stone, RANGE_RENAME ))
{
// Let the stone’s normal menu handle rename; feels like RunUO’s prompt
socket.SysMessage( "Opening ranch menu for renaming..." );
socket.tempObj = stone;
TriggerEvent( 19602, "OpenRanchStoneGump", pUser, stone, 1, null );
return false;
}
// If not near stone, just message
socket.SysMessage( "Equip the hammer to work on fences, or stand near your ranch stone to rename." );
return false;
}
// Must be equipped to target fences/gates
if( iUsed.container != pUser )
{
socket.SysMessage( "You must equip that in order to use it." );
return false;
}
// Bound stone present & in range?
if( !ValidateObject( stone ))
{
socket.SysMessage( "This hammer is not bound to a ranch stone. Claim one from your ranch stone." );
return false;
}
var ranchSize = TriggerEvent( 19602, "GetRanchSize", stone );
if( !IsPlayerInsideRanch(pUser, stone) )
{
// Double-tier warning
if( pUser.InRange(stone, ranchSize + 10) )
socket.SysMessage("You are WELL out of range of your Ranch Stone!");
else
socket.SysMessage("You are out of range of your Ranch Stone.");
socket.SysMessage("Stone. " + stone);
return false;
}
// Optional house check – uncomment if you want to require working inside same house:
// if( !IsSameHouseAsStone( pUser, stone )) { socket.SysMessage("You must be inside your ranch house."); return true; }
socket.CustomTarget( 0, "Target the fence or gate to hammer in (or knock free)." );
// Remember which hammer is asking
pUser.SetTempTag( "fh_ser", iUsed.serial.toString() );
return false;
}
// Target callback
function onCallback0( pSock, myTarget )
{
var pUser = pSock.currentChar;
var fhSer = parseInt( pUser.GetTempTag( "fh_ser" ) || "0", 10 );
pUser.SetTempTag( "fh_ser", "" );
if( !fhSer ) return;
var hammer = CalcItemFromSer ? CalcItemFromSer( fhSer ) : null;
if( !ValidateObject( hammer )) return;
var stone = GetBoundStoneFromHammer( hammer );
if( !ValidateObject( stone )) { pSock.SysMessage("This hammer is no longer bound to a ranch."); return; }
var r = TriggerEvent( 19602, "GetRanchSize", stone );
if( !myTarget.InRange( stone, r ) )
{
pSock.SysMessage( "That fence is outside your ranch boundary (size " + r + ")." );
return;
}
// If the player targets the ranch stone itself: open rename UI
if( myTarget && myTarget.isItem && myTarget.sectionID == stone.sectionID && myTarget.serial == stone.serial )
{
TriggerEvent( 19602, "OpenRanchStoneGump", pUser, stone, 1, null );
return;
}
// Expecting a fence/gate item. We’ll accept any item and toggle if allowed.
if( !myTarget || !myTarget.isItem )
{
pSock.SysMessage( "You can't do that." );
return;
}
// Only allow the 4 fence sections you specified
if( !IsWhitelistedFence( myTarget ))
{
pSock.SysMessage( "You can only use this on ranch fence pieces." );
return;
}
// Toggle movable and set/clear ranch tag
// (Your shard may limit which sectionIDs are valid; you can whitelist if desired.)
if( myTarget.movable == 1 )
{
// Trying to hammer in (lock) must be within ranch radius from the stone
var radius = TriggerEvent( 19602, "GetRanchSize", stone );
if( !myTarget.InRange( stone, radius ) ) // or stone.InRange(myTarget, radius)
{
pSock.SysMessage( "That fence is outside your ranch boundary (size " + radius + ")." );
return;
}
pUser.SoundEffect( 0x0136, true ); // “hammer in”
myTarget.movable = 2;
myTarget.decayable = false;
myTarget.Refresh();
myTarget.SetTag( FENCE_TAG, stone.serial );
pSock.SysMessage( "You set the fence into the ground." );
}
else
{
pUser.SoundEffect( 0x013F, true ); // “knock free”
myTarget.movable = 1;
myTarget.decayable = true;
myTarget.Refresh();
myTarget.SetTag( FENCE_TAG, null );
pSock.SysMessage( "You knock it free from the ground." );
}
}
// Fencing Hammer (UOX3 - SpiderMonkey 1.8.5)
// ScriptID: 19603
// File: js/custom/fencinghammer.js
// ============================================================================
var FENCE_TAG = "ranchStoneSerial"; // set on fence when “hammered in”
var STONE_TAG = "ranchStoneSerial"; // stored on hammer to bind it to a stone
var RANGE_TO_STONE_USE = 10; // working range for targeting
var RANGE_RENAME = 5; // “in pack & near stone” quick rename distance
// --- Add near the top with your other constants ---
var FENCE_SECTIONS = {
"Fence": true,
"Fencetwo": true,
"FenceCorner": true,
"FencePost": true
};
function GetRanchSize( iStone )
{
var v = parseInt( iStone.GetTag( "ranchSize" ), 10 );
if( isNaN( v ) || v <= 0 )
v = SIZE_DEFAULT || 10; // fallback if constants not shared here
return v;
}
// Recognize a valid fence/gate item by sectionID
function IsWhitelistedFence( it )
{
return ( it && it.isItem && !!FENCE_SECTIONS[ it.sectionID ] );
}
function GetBoundStoneFromHammer( hammer )
{
if( !ValidateObject( hammer )) return null;
var ser = parseInt( hammer.GetTag( STONE_TAG ), 10 );
if( !ser || ser <= 0 ) return null;
// Try to locate the stone by serial
var stone = CalcItemFromSer ? CalcItemFromSer( ser ) : null;
if( ValidateObject( stone )) return stone;
// Fallback: if not resolvable by serial, at least ensure player is in same multi as stored coords
return null;
}
function IsOwnerNearStone( pChar, stone, maxRange )
{
if( !ValidateObject( pChar ) || !ValidateObject( stone )) return false;
if( pChar.serial != parseInt( stone.GetTag( "ownerSerial" ), 10 )) return false;
return stone.InRange( pChar, maxRange|0 );
}
// Optional: require same house (multi) as stone
function IsSameHouseAsStone( who, stone )
{
var mStone = FindMulti( stone );
var mUser = FindMulti( who );
if( !mStone || !mUser ) return false;
// Basic equality (many shards just compare pointers or serials if exposed)
return ( mStone.serial && mUser.serial && ( mStone.serial == mUser.serial ));
}
function IsPlayerInsideRanch( pUser, stone )
{
if( !ValidateObject(pUser) || !ValidateObject(stone) )
return false;
var radius = TriggerEvent( 19602, "GetRanchSize", stone );
return pUser.InRange(stone, radius);
}
// --- Main use handler ---
function onUseChecked( pUser, iUsed )
{
if( !iUsed || !iUsed.isItem ) return false;
// Only respond to fencing hammer section
if( iUsed.sectionID != "fencinghammer" )
return false;
var socket = pUser.socket;
var stone = GetBoundStoneFromHammer( iUsed );
// If hammer is in pack and near the bound stone, allow quick rename via stone
if( iUsed.container && iUsed.container.isItem && iUsed.container == pUser.pack )
{
if( ValidateObject( stone ) && IsOwnerNearStone( pUser, stone, RANGE_RENAME ))
{
// Let the stone’s normal menu handle rename; feels like RunUO’s prompt
socket.SysMessage( "Opening ranch menu for renaming..." );
socket.tempObj = stone;
TriggerEvent( 19602, "OpenRanchStoneGump", pUser, stone, 1, null );
return false;
}
// If not near stone, just message
socket.SysMessage( "Equip the hammer to work on fences, or stand near your ranch stone to rename." );
return false;
}
// Must be equipped to target fences/gates
if( iUsed.container != pUser )
{
socket.SysMessage( "You must equip that in order to use it." );
return false;
}
// Bound stone present & in range?
if( !ValidateObject( stone ))
{
socket.SysMessage( "This hammer is not bound to a ranch stone. Claim one from your ranch stone." );
return false;
}
var ranchSize = TriggerEvent( 19602, "GetRanchSize", stone );
if( !IsPlayerInsideRanch(pUser, stone) )
{
// Double-tier warning
if( pUser.InRange(stone, ranchSize + 10) )
socket.SysMessage("You are WELL out of range of your Ranch Stone!");
else
socket.SysMessage("You are out of range of your Ranch Stone.");
socket.SysMessage("Stone. " + stone);
return false;
}
// Optional house check – uncomment if you want to require working inside same house:
// if( !IsSameHouseAsStone( pUser, stone )) { socket.SysMessage("You must be inside your ranch house."); return true; }
socket.CustomTarget( 0, "Target the fence or gate to hammer in (or knock free)." );
// Remember which hammer is asking
pUser.SetTempTag( "fh_ser", iUsed.serial.toString() );
return false;
}
// Target callback
function onCallback0( pSock, myTarget )
{
var pUser = pSock.currentChar;
var fhSer = parseInt( pUser.GetTempTag( "fh_ser" ) || "0", 10 );
pUser.SetTempTag( "fh_ser", "" );
if( !fhSer ) return;
var hammer = CalcItemFromSer ? CalcItemFromSer( fhSer ) : null;
if( !ValidateObject( hammer )) return;
var stone = GetBoundStoneFromHammer( hammer );
if( !ValidateObject( stone )) { pSock.SysMessage("This hammer is no longer bound to a ranch."); return; }
var r = TriggerEvent( 19602, "GetRanchSize", stone );
if( !myTarget.InRange( stone, r ) )
{
pSock.SysMessage( "That fence is outside your ranch boundary (size " + r + ")." );
return;
}
// If the player targets the ranch stone itself: open rename UI
if( myTarget && myTarget.isItem && myTarget.sectionID == stone.sectionID && myTarget.serial == stone.serial )
{
TriggerEvent( 19602, "OpenRanchStoneGump", pUser, stone, 1, null );
return;
}
// Expecting a fence/gate item. We’ll accept any item and toggle if allowed.
if( !myTarget || !myTarget.isItem )
{
pSock.SysMessage( "You can't do that." );
return;
}
// Only allow the 4 fence sections you specified
if( !IsWhitelistedFence( myTarget ))
{
pSock.SysMessage( "You can only use this on ranch fence pieces." );
return;
}
// Toggle movable and set/clear ranch tag
// (Your shard may limit which sectionIDs are valid; you can whitelist if desired.)
if( myTarget.movable == 1 )
{
// Trying to hammer in (lock) must be within ranch radius from the stone
var radius = TriggerEvent( 19602, "GetRanchSize", stone );
if( !myTarget.InRange( stone, radius ) ) // or stone.InRange(myTarget, radius)
{
pSock.SysMessage( "That fence is outside your ranch boundary (size " + radius + ")." );
return;
}
pUser.SoundEffect( 0x0136, true ); // “hammer in”
myTarget.movable = 2;
myTarget.decayable = false;
myTarget.Refresh();
myTarget.SetTag( FENCE_TAG, stone.serial );
pSock.SysMessage( "You set the fence into the ground." );
}
else
{
pUser.SoundEffect( 0x013F, true ); // “knock free”
myTarget.movable = 1;
myTarget.decayable = true;
myTarget.Refresh();
myTarget.SetTag( FENCE_TAG, null );
pSock.SysMessage( "You knock it free from the ground." );
}
}
// ============================================================================
// RanchStone System for UOX3 (SpiderMonkey 1.8.5)
// ScriptID: 19602
// File: js/custom/ranchstone.js
// ============================================================================
// ------------------------------
// Config
// ------------------------------
var RANCH_STONE_SECTION = "ranchstone";
var RANCH_DEED_SECTION = "ranchdeed";
var NEARBY_PLAYER_RANGE = 12;
var TITLE_MAXLEN = 24;
var SIZE_DEFAULT = 10; // starting radius
var SIZE_STEP = 5; // how much each upgrade adds
var SIZE_MAX = 50; // hard cap (tune as you like)
var UPGRADE_SECTION = "ranchupgrade"; // DFN section name for the upgrade token/deed
var GUMP_W = 300;
var GUMP_H = 260;
var FENCING_HAMMER_SECTION = "fencinghammer"; // DFN for the hammer item
// ------------------------------
// Helpers
// ------------------------------
function GetMultiAtObject( obj )
{
// returns MULTI or null
if( !obj ) return null;
return FindMulti( obj ); // easiest form: pass the object
}
function GetMultiAtCoords( x, y, z, world, instanceID )
{
// returns MULTI or null
if( typeof instanceID === "number" )
return FindMulti( x, y, z, world, instanceID );
return FindMulti( x, y, z, world ); // common case
}
function IsBadPlacementRegion( region )
{
if( !region )
return true;
// Adjust to your rules
if( region.town )
return true;
if( region.guarded )
return true;
if( region.dungeon )
return true;
if( region.house )
return true;
return false;
}
function InFrontOf( pUser )
{
var x = pUser.x;
var y = pUser.y;
switch( pUser.dir )
{
case 0: y -= 1; break; // N
case 1: x += 1; break; // E
case 2: y += 1; break; // S
case 3: x -= 1; break; // W
case 4: y -= 1; break; // NE
case 5: x += 1; break; // SE
case 6: y += 1; break; // SW
case 7: x -= 1; break; // NW
}
return { x: x, y: y, z: pUser.z };
}
function MakeDefaultRanchName( pUser )
{
return ( pUser && pUser.name ? pUser.name : "Player" ) + "'s Ranch";
}
function ValidateOwner( pChar, iStone )
{
if( !ValidateObject( pChar ) || !ValidateObject( iStone ))
return false;
var ownerSer = parseInt( iStone.GetTag( "ownerSerial" ), 10 );
return ownerSer === pChar.serial;
}
function GetNearbyPlayers( pUser, range )
{
var list = [];
if( !ValidateObject( pUser ))
return list;
if( typeof AreaCharFunction === "function" )
{
AreaCharFunction( pUser, range, function( ch )
{
if( ch && ch.isChar && ch.online && ch.serial !== pUser.serial )
list.push( ch );
return false; // continue
});
}
return list;
}
function MakeTransferBtnId( idx ){ return 100 + idx; }
function IsTransferBtnId( btn ){ return btn >= 100 && btn < 150; } // up to 50 entries
function TransferIdxFromBtn( btn ){ return btn - 100; }
function GetRanchSize( iStone )
{
var v = parseInt( iStone.GetTag( "ranchSize" ), 10 );
if( isNaN( v ) || v <= 0 )
v = SIZE_DEFAULT;
return v;
}
function SetRanchSize( iStone, size )
{
if( size < 1 ) size = 1;
if( size > SIZE_MAX ) size = SIZE_MAX;
iStone.SetTag( "ranchSize", size );
}
function CalcRanchArea( size )
{
return Math.floor( size * size * 3.14159 );
}
function FindPackItemBySection( pUser, sectionID )
{
var pack = pUser ? pUser.pack : null;
if( !ValidateObject( pack ))
return null;
var it;
for( it = pack.FirstItem(); !pack.FinishedItems(); it = pack.NextItem() )
{
if( !ValidateObject( it )) continue;
if( it.sectionID == sectionID )
return it;
}
return null;
}
function TryUpgradeRanchSize( pSock, pUser, iStone )
{
var cur = GetRanchSize( iStone );
if( cur >= SIZE_MAX )
{
pSock.SysMessage( "This ranch is already at maximum size." );
return false;
}
// Require an upgrade token in the backpack
var token = FindPackItemBySection( pUser, UPGRADE_SECTION );
if( !ValidateObject( token ))
{
pSock.SysMessage( "You need a Ranch Size Upgrade to increase size." );
return false;
}
// Consume token and increase size
token.Delete();
var next = cur + SIZE_STEP;
if( next > SIZE_MAX ) next = SIZE_MAX;
SetRanchSize( iStone, next );
pSock.SysMessage( "Ranch size increased to " + next + " (area " + CalcRanchArea( next ) + ")." );
return true;
}
// ------------------------------
// Gump
// ------------------------------
function OpenRanchStoneGump( pUser, iStone, page, playersCache )
{
if( !ValidateObject( pUser ) || !ValidateObject( iStone ))
return;
// Fetch and show size/area
var ranchSize = GetRanchSize( iStone );
var ranchArea = CalcRanchArea( ranchSize );
if( page !== 2 )
page = 1;
var g = new Gump;
g.AddBackground( 0, 0, GUMP_W, GUMP_H, 9200 );
//g.AddCheckerTrans( 8, 8, GUMP_W - 16, GUMP_H - 16 );
var ranchName = iStone.GetTag( "ranchName" ) || "Ranch";
var ownerSer = parseInt( iStone.GetTag( "ownerSerial" ), 10 );
var header = ranchName + " (Owner: " + ( ownerSer ? ( "0x" + ownerSer.toString( 16 ).toUpperCase()) : "Unset" ) + ")";
g.AddText( 16, 16, 0, header ); // TextID 0
if( page === 1 )
{
g.AddText( 16, 48, 0, "Rename:" ); // TextID 1
g.AddButton( 16, 80, 1209, 1, 1, 0, 1 ); g.AddText( 42, 82, 0, "Save Name" ); // pButton=1
g.AddButton( 16, 110, 1209, 1, 1, 0, 4 ); g.AddText( 42, 112, 0, "Upgrade Size" ); // pButton=4 <-- NE
g.AddButton( 160, 110, 1209, 1, 1, 0, 5 ); g.AddText( 186, 112, 0, "Get Hammer" ); // pButton=5
g.AddButton( 16, 140, 1209, 1, 1, 0, 2 ); g.AddText( 42, 142, 0, "Redeed Ranch" ); // pButton=2
g.AddButton( 16, 210, 1209, 1, 1, 0, 3 ); g.AddText( 42, 212, 0, "Transfer Ownership" ); // pButton=3
g.AddButton( 200, 210, 0xA50, 1, 1, 0, 0 ); g.AddText( 226, 212, 0, "Close" ); // pButton=0
g.AddText( 16, 180, 0, "Size: " + ranchSize ); // like AddLabel in RunUO
g.AddText( 120, 180, 0, "Area: " + ranchArea );
g.AddTextEntryLimited( 80, 46, 200, 20, 0, 1, 10, ranchName, TITLE_MAXLEN ); // textID=2
}
else
{
g.AddText( 16, 48, 0, "Transfer to nearby player:" ); // TextID 1
var list = playersCache || GetNearbyPlayers( pUser, NEARBY_PLAYER_RANGE );
var y = 70;
if( list && list.length )
{
var max = Math.min( list.length, 10 );
for( var i = 0; i < max; ++i )
{
var ch = list[i];
var nm = ch.name || ( "0x" + ch.serial.toString( 16 ).toUpperCase());
g.AddButton( 16, y, 0xF7, 1, 0, MakeTransferBtnId( i ), 0 );
g.AddText( 42, y + 2, 0, nm );
y += 24;
}
}
else
{
g.AddText( 16, 72, 0, "None in range." );
y += 24;
}
g.AddButton( 16, GUMP_H - 50, 1209, 1, 1, 0, 9 ); g.AddText( 42, GUMP_H - 48, 0, "Back" );
g.AddButton( 200, GUMP_H - 50, 0xA50, 1, 1, 0, 0 ); g.AddText( 226, GUMP_H - 48, 0, "Close" );
// Cache serials on socket temp tags
if( list && list.length )
{
var serList = [];
for( var k = 0; k < list.length; ++k )
serList.push( list[k].serial );
pUser.SetTempTag( "ranch_transfer_candidates", serList.join( "," ) );
}
else
{
pUser.SetTempTag( "ranch_transfer_candidates", "" );
}
}
pUser.socket.tempObj = iStone;
g.Send( pUser.socket );
g.Free();
}
// ------------------------------
// Events
// ------------------------------
function onUseChecked( pUser, iUsed )
{
// Deed -> place stone
if( iUsed.sectionID == RANCH_DEED_SECTION )
{
var socket = pUser.socket;
//if( !iUsed.InRange( pUser, 2 ))
// return false;
// Must be standing inside a multi (house)
//var iMulti = GetMultiAtObject( pUser );
//if( !iMulti )
//{
// socket.SysMessage( "You must be inside a house to place this." );
// return false;
//}
//if( IsBadPlacementRegion( pUser.region ))
//{
// socket.SysMessage( "You cannot place that here." );
// return false;
//}
var spot = InFrontOf( pUser );
var stone = CreateDFNItem( null, null, RANCH_STONE_SECTION, 1, "ITEM", false, 0, pUser.worldnumber, pUser.instanceID );
if( !ValidateObject( stone ))
{
socket.SysMessage( "Failed to place the ranch stone." );
return false;
}
stone.Teleport( spot.x, spot.y, spot.z, pUser.worldnumber );
stone.SetTag( "ownerSerial", pUser.serial );
stone.SetTag( "ranchName", MakeDefaultRanchName( pUser ) );
stone.SetTag( "ranchSize", SIZE_DEFAULT );
socket.SysMessage( "You place your ranch stone." );
iUsed.Delete();
return true;
}
// Stone -> open gump
if( iUsed && iUsed.isItem && iUsed.sectionID == RANCH_STONE_SECTION )
{
if( !ValidateOwner( pUser, iUsed ))
{
pUser.socket.SysMessage( "You are not the owner of this ranch." );
return false;
}
OpenRanchStoneGump( pUser, iUsed, 1, null );
return false;
}
return false;
}
function onGumpPress( pSock, pButton, gumpData )
{
var pUser = pSock.currentChar;
var iStone = pSock.tempObj;
if( !ValidateObject( pUser ) || !ValidateObject( iStone ))
return;
if( pButton == 0 )
return;
// Save Name
if( pButton == 1 )
{
var newName = gumpData.getEdit( 0 ) || "";
newName = newName.trim();
if( newName.length == 0 )
newName = "Ranch";
if( newName.length > TITLE_MAXLEN )
newName = newName.substring( 0, TITLE_MAXLEN );
iStone.SetTag( "ranchName", newName );
iStone.name = newName;
pSock.SysMessage( "Ranch renamed to: " + newName );
OpenRanchStoneGump( pUser, iStone, 1, null );
return;
}
// Redeed
if( pButton == 2 )
{
var deed = CreateDFNItem( pSock, pUser, RANCH_DEED_SECTION, 1, "ITEM", true );
if( ValidateObject( deed ))
{
pSock.SysMessage( "Your ranch has been redeeded." );
iStone.Delete();
}
else
{
pSock.SysMessage( "Failed to create the deed." );
}
return;
}
// Transfer menu
if( pButton == 3 )
{
var list = GetNearbyPlayers( pUser, NEARBY_PLAYER_RANGE );
OpenRanchStoneGump( pUser, iStone, 2, list );
return;
}
// Upgrade Size
if( pButton == 4 )
{
if( TryUpgradeRanchSize( pSock, pUser, iStone ) )
{
// Refresh page to show new size/area
OpenRanchStoneGump( pUser, iStone, 1, null );
}
return;
}
// Get Fencing Hammer (bound)
if( pButton == 5 )
{
if( !ValidateOwner( pUser, iStone ))
{
pSock.SysMessage( "Only the ranch owner can claim the hammer." );
return;
}
if( GiveFencingHammerBoundToStone( pSock, pUser, iStone ))
OpenRanchStoneGump( pUser, iStone, 1, null ); // refresh
return;
}
// Back
if( pButton == 9 )
{
OpenRanchStoneGump( pUser, iStone, 1, null );
return;
}
// Transfer to specific player
if( IsTransferBtnId( pButton ))
{
var idx = TransferIdxFromBtn( pButton );
var serCsv = pSock.GetTempTag( "ranch_transfer_candidates" ) || "";
if( serCsv.length == 0 )
{
pSock.SysMessage( "No nearby candidates found." );
OpenRanchStoneGump( pUser, iStone, 2, [] );
return;
}
var parts = serCsv.split( "," );
if( idx < 0 || idx >= parts.length )
{
pSock.SysMessage( "Invalid selection." );
OpenRanchStoneGump( pUser, iStone, 2, [] );
return;
}
var targSer = parseInt( parts[idx], 10 );
var targ = CalcCharFromSer( targSer );
if( !ValidateObject( targ ) || !targ.online || !targ.InRange( pUser, NEARBY_PLAYER_RANGE ))
{
pSock.SysMessage( "That player is no longer valid or in range." );
OpenRanchStoneGump( pUser, iStone, 2, GetNearbyPlayers( pUser, NEARBY_PLAYER_RANGE ) );
return;
}
iStone.SetTag( "ownerSerial", targ.serial );
var oldName = iStone.GetTag( "ranchName" ) || "Ranch";
var suffix = "'s Ranch";
var newName;
if( oldName.length >= suffix.length && oldName.indexOf( suffix, oldName.length - suffix.length ) != -1 )
newName = MakeDefaultRanchName( targ );
else
newName = oldName;
iStone.SetTag( "ranchName", newName );
pSock.SysMessage( "Ranch ownership transferred to " + ( targ.name || "player" ) + "." );
OpenRanchStoneGump( pUser, iStone, 1, null );
return;
}
pSock.SysMessage( "Unhandled button: " + pButton );
}
function GiveFencingHammerBoundToStone( pSock, pUser, iStone )
{
// Make (or re-make) the hammer in player pack
var hammer = CreateDFNItem( pSock, pUser, FENCING_HAMMER_SECTION, 1, "ITEM", true );
if( !ValidateObject( hammer ))
{
pSock.SysMessage( "Could not create a fencing hammer." );
return false;
}
// Bind the hammer to this ranch stone so it knows which stone to range-check
hammer.SetTag( "ranchStoneSerial", iStone.serial );
hammer.SetTag( "ranchStoneWorld", iStone.worldnumber );
hammer.SetTag( "ranchStoneX", iStone.x );
hammer.SetTag( "ranchStoneY", iStone.y );
hammer.SetTag( "ranchStoneZ", iStone.z );
// Cosmetic
if( !hammer.name || hammer.name == "" )
hammer.name = "a fencing hammer";
pSock.SysMessage( "A fencing hammer bound to this ranch has been placed in your pack." );
return true;
}
// RanchStone System for UOX3 (SpiderMonkey 1.8.5)
// ScriptID: 19602
// File: js/custom/ranchstone.js
// ============================================================================
// ------------------------------
// Config
// ------------------------------
var RANCH_STONE_SECTION = "ranchstone";
var RANCH_DEED_SECTION = "ranchdeed";
var NEARBY_PLAYER_RANGE = 12;
var TITLE_MAXLEN = 24;
var SIZE_DEFAULT = 10; // starting radius
var SIZE_STEP = 5; // how much each upgrade adds
var SIZE_MAX = 50; // hard cap (tune as you like)
var UPGRADE_SECTION = "ranchupgrade"; // DFN section name for the upgrade token/deed
var GUMP_W = 300;
var GUMP_H = 260;
var FENCING_HAMMER_SECTION = "fencinghammer"; // DFN for the hammer item
// ------------------------------
// Helpers
// ------------------------------
function GetMultiAtObject( obj )
{
// returns MULTI or null
if( !obj ) return null;
return FindMulti( obj ); // easiest form: pass the object
}
function GetMultiAtCoords( x, y, z, world, instanceID )
{
// returns MULTI or null
if( typeof instanceID === "number" )
return FindMulti( x, y, z, world, instanceID );
return FindMulti( x, y, z, world ); // common case
}
function IsBadPlacementRegion( region )
{
if( !region )
return true;
// Adjust to your rules
if( region.town )
return true;
if( region.guarded )
return true;
if( region.dungeon )
return true;
if( region.house )
return true;
return false;
}
function InFrontOf( pUser )
{
var x = pUser.x;
var y = pUser.y;
switch( pUser.dir )
{
case 0: y -= 1; break; // N
case 1: x += 1; break; // E
case 2: y += 1; break; // S
case 3: x -= 1; break; // W
case 4: y -= 1; break; // NE
case 5: x += 1; break; // SE
case 6: y += 1; break; // SW
case 7: x -= 1; break; // NW
}
return { x: x, y: y, z: pUser.z };
}
function MakeDefaultRanchName( pUser )
{
return ( pUser && pUser.name ? pUser.name : "Player" ) + "'s Ranch";
}
function ValidateOwner( pChar, iStone )
{
if( !ValidateObject( pChar ) || !ValidateObject( iStone ))
return false;
var ownerSer = parseInt( iStone.GetTag( "ownerSerial" ), 10 );
return ownerSer === pChar.serial;
}
function GetNearbyPlayers( pUser, range )
{
var list = [];
if( !ValidateObject( pUser ))
return list;
if( typeof AreaCharFunction === "function" )
{
AreaCharFunction( pUser, range, function( ch )
{
if( ch && ch.isChar && ch.online && ch.serial !== pUser.serial )
list.push( ch );
return false; // continue
});
}
return list;
}
function MakeTransferBtnId( idx ){ return 100 + idx; }
function IsTransferBtnId( btn ){ return btn >= 100 && btn < 150; } // up to 50 entries
function TransferIdxFromBtn( btn ){ return btn - 100; }
function GetRanchSize( iStone )
{
var v = parseInt( iStone.GetTag( "ranchSize" ), 10 );
if( isNaN( v ) || v <= 0 )
v = SIZE_DEFAULT;
return v;
}
function SetRanchSize( iStone, size )
{
if( size < 1 ) size = 1;
if( size > SIZE_MAX ) size = SIZE_MAX;
iStone.SetTag( "ranchSize", size );
}
function CalcRanchArea( size )
{
return Math.floor( size * size * 3.14159 );
}
function FindPackItemBySection( pUser, sectionID )
{
var pack = pUser ? pUser.pack : null;
if( !ValidateObject( pack ))
return null;
var it;
for( it = pack.FirstItem(); !pack.FinishedItems(); it = pack.NextItem() )
{
if( !ValidateObject( it )) continue;
if( it.sectionID == sectionID )
return it;
}
return null;
}
function TryUpgradeRanchSize( pSock, pUser, iStone )
{
var cur = GetRanchSize( iStone );
if( cur >= SIZE_MAX )
{
pSock.SysMessage( "This ranch is already at maximum size." );
return false;
}
// Require an upgrade token in the backpack
var token = FindPackItemBySection( pUser, UPGRADE_SECTION );
if( !ValidateObject( token ))
{
pSock.SysMessage( "You need a Ranch Size Upgrade to increase size." );
return false;
}
// Consume token and increase size
token.Delete();
var next = cur + SIZE_STEP;
if( next > SIZE_MAX ) next = SIZE_MAX;
SetRanchSize( iStone, next );
pSock.SysMessage( "Ranch size increased to " + next + " (area " + CalcRanchArea( next ) + ")." );
return true;
}
// ------------------------------
// Gump
// ------------------------------
function OpenRanchStoneGump( pUser, iStone, page, playersCache )
{
if( !ValidateObject( pUser ) || !ValidateObject( iStone ))
return;
// Fetch and show size/area
var ranchSize = GetRanchSize( iStone );
var ranchArea = CalcRanchArea( ranchSize );
if( page !== 2 )
page = 1;
var g = new Gump;
g.AddBackground( 0, 0, GUMP_W, GUMP_H, 9200 );
//g.AddCheckerTrans( 8, 8, GUMP_W - 16, GUMP_H - 16 );
var ranchName = iStone.GetTag( "ranchName" ) || "Ranch";
var ownerSer = parseInt( iStone.GetTag( "ownerSerial" ), 10 );
var header = ranchName + " (Owner: " + ( ownerSer ? ( "0x" + ownerSer.toString( 16 ).toUpperCase()) : "Unset" ) + ")";
g.AddText( 16, 16, 0, header ); // TextID 0
if( page === 1 )
{
g.AddText( 16, 48, 0, "Rename:" ); // TextID 1
g.AddButton( 16, 80, 1209, 1, 1, 0, 1 ); g.AddText( 42, 82, 0, "Save Name" ); // pButton=1
g.AddButton( 16, 110, 1209, 1, 1, 0, 4 ); g.AddText( 42, 112, 0, "Upgrade Size" ); // pButton=4 <-- NE
g.AddButton( 160, 110, 1209, 1, 1, 0, 5 ); g.AddText( 186, 112, 0, "Get Hammer" ); // pButton=5
g.AddButton( 16, 140, 1209, 1, 1, 0, 2 ); g.AddText( 42, 142, 0, "Redeed Ranch" ); // pButton=2
g.AddButton( 16, 210, 1209, 1, 1, 0, 3 ); g.AddText( 42, 212, 0, "Transfer Ownership" ); // pButton=3
g.AddButton( 200, 210, 0xA50, 1, 1, 0, 0 ); g.AddText( 226, 212, 0, "Close" ); // pButton=0
g.AddText( 16, 180, 0, "Size: " + ranchSize ); // like AddLabel in RunUO
g.AddText( 120, 180, 0, "Area: " + ranchArea );
g.AddTextEntryLimited( 80, 46, 200, 20, 0, 1, 10, ranchName, TITLE_MAXLEN ); // textID=2
}
else
{
g.AddText( 16, 48, 0, "Transfer to nearby player:" ); // TextID 1
var list = playersCache || GetNearbyPlayers( pUser, NEARBY_PLAYER_RANGE );
var y = 70;
if( list && list.length )
{
var max = Math.min( list.length, 10 );
for( var i = 0; i < max; ++i )
{
var ch = list[i];
var nm = ch.name || ( "0x" + ch.serial.toString( 16 ).toUpperCase());
g.AddButton( 16, y, 0xF7, 1, 0, MakeTransferBtnId( i ), 0 );
g.AddText( 42, y + 2, 0, nm );
y += 24;
}
}
else
{
g.AddText( 16, 72, 0, "None in range." );
y += 24;
}
g.AddButton( 16, GUMP_H - 50, 1209, 1, 1, 0, 9 ); g.AddText( 42, GUMP_H - 48, 0, "Back" );
g.AddButton( 200, GUMP_H - 50, 0xA50, 1, 1, 0, 0 ); g.AddText( 226, GUMP_H - 48, 0, "Close" );
// Cache serials on socket temp tags
if( list && list.length )
{
var serList = [];
for( var k = 0; k < list.length; ++k )
serList.push( list[k].serial );
pUser.SetTempTag( "ranch_transfer_candidates", serList.join( "," ) );
}
else
{
pUser.SetTempTag( "ranch_transfer_candidates", "" );
}
}
pUser.socket.tempObj = iStone;
g.Send( pUser.socket );
g.Free();
}
// ------------------------------
// Events
// ------------------------------
function onUseChecked( pUser, iUsed )
{
// Deed -> place stone
if( iUsed.sectionID == RANCH_DEED_SECTION )
{
var socket = pUser.socket;
//if( !iUsed.InRange( pUser, 2 ))
// return false;
// Must be standing inside a multi (house)
//var iMulti = GetMultiAtObject( pUser );
//if( !iMulti )
//{
// socket.SysMessage( "You must be inside a house to place this." );
// return false;
//}
//if( IsBadPlacementRegion( pUser.region ))
//{
// socket.SysMessage( "You cannot place that here." );
// return false;
//}
var spot = InFrontOf( pUser );
var stone = CreateDFNItem( null, null, RANCH_STONE_SECTION, 1, "ITEM", false, 0, pUser.worldnumber, pUser.instanceID );
if( !ValidateObject( stone ))
{
socket.SysMessage( "Failed to place the ranch stone." );
return false;
}
stone.Teleport( spot.x, spot.y, spot.z, pUser.worldnumber );
stone.SetTag( "ownerSerial", pUser.serial );
stone.SetTag( "ranchName", MakeDefaultRanchName( pUser ) );
stone.SetTag( "ranchSize", SIZE_DEFAULT );
socket.SysMessage( "You place your ranch stone." );
iUsed.Delete();
return true;
}
// Stone -> open gump
if( iUsed && iUsed.isItem && iUsed.sectionID == RANCH_STONE_SECTION )
{
if( !ValidateOwner( pUser, iUsed ))
{
pUser.socket.SysMessage( "You are not the owner of this ranch." );
return false;
}
OpenRanchStoneGump( pUser, iUsed, 1, null );
return false;
}
return false;
}
function onGumpPress( pSock, pButton, gumpData )
{
var pUser = pSock.currentChar;
var iStone = pSock.tempObj;
if( !ValidateObject( pUser ) || !ValidateObject( iStone ))
return;
if( pButton == 0 )
return;
// Save Name
if( pButton == 1 )
{
var newName = gumpData.getEdit( 0 ) || "";
newName = newName.trim();
if( newName.length == 0 )
newName = "Ranch";
if( newName.length > TITLE_MAXLEN )
newName = newName.substring( 0, TITLE_MAXLEN );
iStone.SetTag( "ranchName", newName );
iStone.name = newName;
pSock.SysMessage( "Ranch renamed to: " + newName );
OpenRanchStoneGump( pUser, iStone, 1, null );
return;
}
// Redeed
if( pButton == 2 )
{
var deed = CreateDFNItem( pSock, pUser, RANCH_DEED_SECTION, 1, "ITEM", true );
if( ValidateObject( deed ))
{
pSock.SysMessage( "Your ranch has been redeeded." );
iStone.Delete();
}
else
{
pSock.SysMessage( "Failed to create the deed." );
}
return;
}
// Transfer menu
if( pButton == 3 )
{
var list = GetNearbyPlayers( pUser, NEARBY_PLAYER_RANGE );
OpenRanchStoneGump( pUser, iStone, 2, list );
return;
}
// Upgrade Size
if( pButton == 4 )
{
if( TryUpgradeRanchSize( pSock, pUser, iStone ) )
{
// Refresh page to show new size/area
OpenRanchStoneGump( pUser, iStone, 1, null );
}
return;
}
// Get Fencing Hammer (bound)
if( pButton == 5 )
{
if( !ValidateOwner( pUser, iStone ))
{
pSock.SysMessage( "Only the ranch owner can claim the hammer." );
return;
}
if( GiveFencingHammerBoundToStone( pSock, pUser, iStone ))
OpenRanchStoneGump( pUser, iStone, 1, null ); // refresh
return;
}
// Back
if( pButton == 9 )
{
OpenRanchStoneGump( pUser, iStone, 1, null );
return;
}
// Transfer to specific player
if( IsTransferBtnId( pButton ))
{
var idx = TransferIdxFromBtn( pButton );
var serCsv = pSock.GetTempTag( "ranch_transfer_candidates" ) || "";
if( serCsv.length == 0 )
{
pSock.SysMessage( "No nearby candidates found." );
OpenRanchStoneGump( pUser, iStone, 2, [] );
return;
}
var parts = serCsv.split( "," );
if( idx < 0 || idx >= parts.length )
{
pSock.SysMessage( "Invalid selection." );
OpenRanchStoneGump( pUser, iStone, 2, [] );
return;
}
var targSer = parseInt( parts[idx], 10 );
var targ = CalcCharFromSer( targSer );
if( !ValidateObject( targ ) || !targ.online || !targ.InRange( pUser, NEARBY_PLAYER_RANGE ))
{
pSock.SysMessage( "That player is no longer valid or in range." );
OpenRanchStoneGump( pUser, iStone, 2, GetNearbyPlayers( pUser, NEARBY_PLAYER_RANGE ) );
return;
}
iStone.SetTag( "ownerSerial", targ.serial );
var oldName = iStone.GetTag( "ranchName" ) || "Ranch";
var suffix = "'s Ranch";
var newName;
if( oldName.length >= suffix.length && oldName.indexOf( suffix, oldName.length - suffix.length ) != -1 )
newName = MakeDefaultRanchName( targ );
else
newName = oldName;
iStone.SetTag( "ranchName", newName );
pSock.SysMessage( "Ranch ownership transferred to " + ( targ.name || "player" ) + "." );
OpenRanchStoneGump( pUser, iStone, 1, null );
return;
}
pSock.SysMessage( "Unhandled button: " + pButton );
}
function GiveFencingHammerBoundToStone( pSock, pUser, iStone )
{
// Make (or re-make) the hammer in player pack
var hammer = CreateDFNItem( pSock, pUser, FENCING_HAMMER_SECTION, 1, "ITEM", true );
if( !ValidateObject( hammer ))
{
pSock.SysMessage( "Could not create a fencing hammer." );
return false;
}
// Bind the hammer to this ranch stone so it knows which stone to range-check
hammer.SetTag( "ranchStoneSerial", iStone.serial );
hammer.SetTag( "ranchStoneWorld", iStone.worldnumber );
hammer.SetTag( "ranchStoneX", iStone.x );
hammer.SetTag( "ranchStoneY", iStone.y );
hammer.SetTag( "ranchStoneZ", iStone.z );
// Cosmetic
if( !hammer.name || hammer.name == "" )
hammer.name = "a fencing hammer";
pSock.SysMessage( "A fencing hammer bound to this ranch has been placed in your pack." );
return true;
}