initial commit

This commit is contained in:
Redo 2022-10-05 16:02:11 -06:00
commit 93d385e859
51 changed files with 11722 additions and 0 deletions

View File

@ -0,0 +1,462 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Switch to this mode
function NDM_BoxSelect::onStartMode(%this, %client, %lastMode)
{
if(%lastMode == $NDM::StackSelect)
{
if(isObject(%client.ndSelection) && %client.ndSelection.brickCount)
{
//Create selection box from the size of the previous selection
%root = %client.ndSelection.rootPosition;
%min = vectorAdd(%root, %client.ndSelection.minSize);
%max = vectorAdd(%root, %client.ndSelection.maxSize);
if(%client.isAdmin)
%limit = $Pref::Server::ND::MaxBoxSizeAdmin;
else
%limit = $Pref::Server::ND::MaxBoxSizePlayer;
if((getWord(%max, 0) - getWord(%min, 0) <= %limit)
&& (getWord(%max, 1) - getWord(%min, 1) <= %limit)
&& (getWord(%max, 2) - getWord(%min, 2) <= %limit))
{
%name = %client.name;
if(getSubStr(%name, strLen(%name - 1), 1) $= "s")
%shapeName = %name @ "' Selection Box";
else
%shapeName = %name @ "'s Selection Box";
%client.ndSelectionBox = ND_SelectionBox(%shapeName);
%client.ndSelectionBox.setSizeAligned(%min, %max, %client.getControlObject());
}
else
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Oops!\n<font:Verdana:17>" @
"\c6Your selection box is limited to \c3" @ mFloor(%limit * 2) @ " \c6studs.", 5);
%client.ndSelection.deleteData();
}
%client.ndSelectionAvailable = false;
}
else if(%lastMode == $NDM::BoxSelectProgress && %client.ndSelection.brickCount > 0)
{
%client.ndSelectionBox.setDisabledMode();
%client.ndSelectionAvailable = true;
}
else if(%lastMode != $NDM::FillColor && %lastMode != $NDM::WrenchProgress)
%client.ndSelectionAvailable = false;
%client.ndLastSelectMode = %this;
%client.ndUpdateBottomPrint();
}
//Switch away from this mode
function NDM_BoxSelect::onChangeMode(%this, %client, %nextMode)
{
if(%nextMode == $NDM::StackSelect)
{
//Clear selection
if(isObject(%client.ndSelection))
%client.ndSelection.deleteData();
//Remove the selection box
if(isObject(%client.ndSelectionBox))
%client.ndSelectionBox.delete();
}
else if(%nextMode == $NDM::PlantCopy)
{
//Start de-highlighting the bricks
%client.ndSelection.deHighlight();
//Remove the selection box
if(isObject(%client.ndSelectionBox))
%client.ndSelectionBox.delete();
}
else if(%nextMode == $NDM::CutProgress)
{
//Remove the selection box
if(isObject(%client.ndSelectionBox))
%client.ndSelectionBox.delete();
}
else if(%nextMode == $NDM::FillColor)
{
//Start de-highlighting the bricks
%client.ndSelection.deHighlight();
}
else if(%nextMode == $NDM::WrenchProgress)
{
//Start de-highlighting the bricks
%client.ndSelection.deHighlight();
}
else if(%nextMode == $NDM::LoadProgress)
{
//Remove the selection box
if(isObject(%client.ndSelectionBox))
%client.ndSelectionBox.delete();
}
}
//Kill this mode
function NDM_BoxSelect::onKillMode(%this, %client)
{
//Destroy selection
if(isObject(%client.ndSelection))
%client.ndSelection.delete();
//Delete the selection box
if(isObject(%client.ndSelectionBox))
%client.ndSelectionBox.delete();
}
//Duplicator image callbacks
///////////////////////////////////////////////////////////////////////////
//Selecting an object with the duplicator
function NDM_BoxSelect::onSelectObject(%this, %client, %obj, %pos, %normal)
{
if((%obj.getType() & $TypeMasks::FxBrickAlwaysObjectType) == 0)
return;
if(!ndTrustCheckMessage(%obj, %client))
return;
if(%client.ndSelectionAvailable)
{
messageClient(%client, 'MsgError', "");
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Selection canceled! " @
"You can now edit the box again.", 5);
%client.ndSelectionAvailable = false;
%client.ndSelection.deleteData();
%client.ndSelectionBox.setNormalMode();
%client.ndUpdateBottomPrint();
}
if(isObject(%client.ndSelectionBox))
{
if(%client.ndMultiSelect)
{
%box1 = %client.ndSelectionBox.getWorldBox();
//%box2 = ndGetPlateBoxFromRayCast(%pos, %normal);
%box2 = %obj.getWorldBox();
%p1 = getMin(getWord(%box1, 0), getWord(%box2, 0))
SPC getMin(getWord(%box1, 1), getWord(%box2, 1))
SPC getMin(getWord(%box1, 2), getWord(%box2, 2));
%p2 = getMax(getWord(%box1, 3), getWord(%box2, 3))
SPC getMax(getWord(%box1, 4), getWord(%box2, 4))
SPC getMax(getWord(%box1, 5), getWord(%box2, 5));
}
else
{
%box = %obj.getWorldBox();
%p1 = getWords(%box, 0, 2);
%p2 = getWords(%box, 3, 5);
}
}
else
{
%name = %client.name;
if(getSubStr(%name, strLen(%name - 1), 1) $= "s")
%shapeName = %name @ "' Selection Box";
else
%shapeName = %name @ "'s Selection Box";
%client.ndSelectionBox = ND_SelectionBox(%shapeName);
// if(%client.ndMultiSelect)
// %box = ndGetPlateBoxFromRayCast(%pos, %normal);
// else
%box = %obj.getWorldBox();
%p1 = getWords(%box, 0, 2);
%p2 = getWords(%box, 3, 5);
}
%client.ndSelectionBox.setSizeAligned(%p1, %p2, %client.getControlObject());
%client.ndUpdateBottomPrint();
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Light key
function NDM_BoxSelect::onLight(%this, %client)
{
if($Pref::Server::ND::PlayMenuSounds)
%client.play2d(lightOffSound);
%client.ndSetMode(NDM_StackSelect);
}
//Prev Seat
function NDM_BoxSelect::onPrevSeat(%this, %client)
{
%client.ndLimited = !%client.ndLimited;
%client.ndUpdateBottomPrint();
if($Pref::Server::ND::PlayMenuSounds)
%client.play2d(%client.ndLimited ? lightOnSound : lightOffSound);
}
//Shift Brick
function NDM_BoxSelect::onShiftBrick(%this, %client, %x, %y, %z)
{
if(!isObject(%client.ndSelectionBox))
return;
//If we have a selection, enter plant mode!
if(%client.ndSelectionAvailable)
{
%client.ndSetMode(NDM_PlantCopy);
NDM_PlantCopy.onShiftBrick(%client, %x, %y, %z);
return;
}
//Move the corner
switch(getAngleIDFromPlayer(%client.getControlObject()))
{
case 0: %newX = %x; %newY = %y;
case 1: %newX = -%y; %newY = %x;
case 2: %newX = -%x; %newY = -%y;
case 3: %newX = %y; %newY = -%x;
}
%newX = mFloor(%newX) / 2;
%newY = mFloor(%newY) / 2;
%z = mFloor(%z ) / 5;
if(!%client.ndMultiSelect)
{
if(%client.isAdmin)
%limit = $Pref::Server::ND::MaxBoxSizeAdmin;
else
%limit = $Pref::Server::ND::MaxBoxSizePlayer;
if(%client.ndSelectionBox.shiftCorner(%newX SPC %newY SPC %z, %limit))
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Oops!\n<font:Verdana:17>" @
"\c6Your selection box is limited to \c3" @ mFloor(%limit * 2) @ " \c6studs.", 5);
%client.ndUpdateBottomPrint();
}
else
{
%client.ndSelectionBox.shift(%newX SPC %newY SPC %z);
}
}
//Super Shift Brick
function NDM_BoxSelect::onSuperShiftBrick(%this, %client, %x, %y, %z)
{
//If we have a selection, enter plant mode!
if(%client.ndSelectionAvailable)
{
%client.ndSetMode(NDM_PlantCopy);
NDM_PlantCopy.onSuperShiftBrick(%client, %x, %y, %z);
return;
}
%this.onShiftBrick(%client, %x * 8, %y * 8, %z * 20);
}
//Rotate Brick
function NDM_BoxSelect::onRotateBrick(%this, %client, %direction)
{
if(!isObject(%client.ndSelectionBox))
return;
//If we have a selection, enter plant mode!
if(%client.ndSelectionAvailable)
{
%client.ndSetMode(NDM_PlantCopy);
NDM_PlantCopy.onRotateBrick(%client, %direction);
return;
}
if(!%client.ndMultiSelect)
%client.ndSelectionBox.switchCorner();
else
{
%client.ndSelectionBox.rotate(%direction);
%client.ndUpdateBottomPrint();
}
}
//Plant Brick
function NDM_BoxSelect::onPlantBrick(%this, %client)
{
if(!isObject(%client.ndSelectionBox))
return;
//If we have a selection, enter plant mode!
if(%client.ndSelectionAvailable)
{
%client.ndSetMode(NDM_PlantCopy);
return;
}
//Check timeout
if(!%client.isAdmin && %client.ndLastSelectTime + ($Pref::Server::ND::SelectTimeoutMS / 1000) > $Sim::Time)
{
%remain = mCeil(%client.ndLastSelectTime + ($Pref::Server::ND::SelectTimeoutMS / 1000) - $Sim::Time);
if(%remain != 1)
%s = "s";
messageClient(%client, 'MsgError', "");
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6You need to wait\c3 " @
%remain @ "\c6 second" @ %s @ " before selecting again!", 5);
return;
}
%client.ndLastSelectTime = $Sim::Time;
//Prepare a selection to copy the bricks
if(isObject(%client.ndSelection))
%client.ndSelection.deleteData();
else
%client.ndSelection = ND_Selection(%client);
//Start selection
%box = %client.ndSelectionBox.getWorldBox();
%client.ndSetMode(NDM_BoxSelectProgress);
%client.ndSelection.startBoxSelection(%box, %client.ndLimited);
}
//Cancel Brick
function NDM_BoxSelect::onCancelBrick(%this, %client)
{
if(!isObject(%client.ndSelectionBox))
return;
if(%client.ndSelectionAvailable)
{
messageClient(%client, 'MsgError', "");
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Selection canceled! " @
"You can now edit the box again.", 5);
%client.ndSelectionAvailable = false;
%client.ndSelection.deleteData();
%client.ndSelectionBox.setNormalMode();
%client.ndUpdateBottomPrint();
return;
}
if(isObject(%client.ndSelection))
%client.ndSelection.deleteData();
%client.ndSelectionBox.delete();
%client.ndSelectionAvailable = false;
%client.ndUpdateBottomPrint();
}
//Copy Selection
function NDM_BoxSelect::onCopy(%this, %client)
{
%this.onPlantBrick(%client);
}
//Cut Selection
function NDM_BoxSelect::onCut(%this, %client)
{
if(!isObject(%client.ndSelectionBox))
return;
if(!%client.ndSelectionAvailable)
{
%this.onPlantBrick(%client);
return;
}
%client.ndSetMode(NDM_CutProgress);
%client.ndSelection.startCutting();
}
//Supercut selection
function NDM_BoxSelect::onSuperCut(%this, %client)
{
if(!isObject(%client.ndSelectionBox))
return;
//Prepare a selection to handle the callback
if(isObject(%client.ndSelection))
%client.ndSelection.deleteData();
else
%client.ndSelection = ND_Selection(%client);
if(!$ND::SimpleBrickTableCreated)
ndCreateSimpleBrickTable();
//Start supercut
%box = %client.ndSelectionBox.getWorldBox();
%client.ndSetMode(NDM_SuperCutProgress);
%client.ndSelection.startSuperCut(%box);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_BoxSelect::getBottomPrint(%this, %client)
{
if(isObject(%client.ndSelection) && %client.ndSelection.brickCount)
{
%count = %client.ndSelection.brickCount;
%title = "Selection Mode (\c3" @ %count @ "\c6 Brick" @ (%count > 1 ? "s)" : ")");
}
else
%title = "Selection Mode";
%l0 = "Type: \c3Box \c6[Light]";
%l1 = "Limited: " @ (%client.ndLimited ? "\c3Yes" : "\c0No") @ " \c6[Prev Seat]";
if(isObject(%client.ndSelectionBox))
{
%size = %client.ndSelectionBox.getSize();
%x = mFloatLength(getWord(%size, 0) * 2, 0);
%y = mFloatLength(getWord(%size, 1) * 2, 0);
%z = mFloatLength(getWord(%size, 2) * 5, 0);
%l2 = "Size: \c3" @ %x @ "\c6 x \c3" @ %y @ "\c6 x \c3" @ %z @ "\c6 Plates";
}
if(!isObject(%client.ndSelectionBox))
{
%r0 = "Click Brick: Place selection box";
%r1 = "";
%r2 = "";
}
else if(!%client.ndSelectionAvailable)
{
%r0 = "[Shift Brick]: Move corner";
%r1 = "[Rotate Brick]: Switch corner";
}
else
{
%r0 = "[Cancel Brick]: Adjust box";
%r1 = "[Plant Brick]: Duplicate";
}
return ndFormatMessage(%title, %l0, %r0, %l1, %r1, %l2, %r2);
}

View File

@ -0,0 +1,59 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Kill this mode
function NDM_BoxSelectProgress::onKillMode(%this, %client)
{
//Destroy the selection
%client.ndSelection.delete();
//Remove selection box
%client.ndSelectionBox.delete();
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Cancel Brick
function NDM_BoxSelectProgress::onCancelBrick(%this, %client)
{
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Selection canceled!", 4);
%client.ndSelection.cancelBoxSelection();
%client.ndSetMode(NDM_BoxSelect);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_BoxSelectProgress::getBottomPrint(%this, %client)
{
%qCount = %client.ndSelection.queueCount;
%bCount = %client.ndSelection.brickCount;
if(%bCount <= 0)
{
%curr = %client.ndSelection.currChunk + 1;
%num = %client.ndSelection.numChunks;
%percent = mFloor(%curr * 100 / %num);
%title = "Searching... (\c3" @ %percent @ "%\c6, \c3" @ %qCount @ "\c6 Bricks)";
}
else
{
%percent = mFloor(%bCount * 100 / %qCount);
%title = "Processing... (\c3" @ %percent @ "%\c6)";
}
%l0 = "[Cancel Brick]: Cancel selection";
return ndFormatMessage(%title, %l0);
}

View File

@ -0,0 +1,48 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Switch away from this mode
function NDM_CutProgress::onChangeMode(%this, %client, %nextMode)
{
if(%nextMode != $NDM::PlantCopy)
%client.ndSelection.deleteData();
}
//Kill this mode
function NDM_CutProgress::onKillMode(%this, %client)
{
//Destroy the selection
%client.ndSelection.delete();
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Cancel Brick
function NDM_CutProgress::onCancelBrick(%this, %client)
{
%client.ndSelection.cancelCutting();
%client.ndSetMode(%client.ndLastSelectMode);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_CutProgress::getBottomPrint(%this, %client)
{
%count = %client.ndSelection.brickCount;
%percent = mFloor(%client.ndSelection.cutIndex * 100 / %count);
%title = "Cutting... (\c3" @ %percent @ "%\c6)";
%l0 = "[Cancel Brick]: Cancel cut";
return ndFormatMessage(%title, %l0);
}

View File

@ -0,0 +1,128 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Switch to this mode
function NDM_FillColor::onStartMode(%this, %client, %lastMode)
{
%client.ndUpdateBottomPrint();
cancel(%client.ndToolSchedule);
}
//Switch away from this mode
function NDM_FillColor::onChangeMode(%this, %client, %nextMode)
{
//Hide paint gui
if(%nextMode != $NDM::FillColorProgress)
{
%client.ndLastEquipTime = $Sim::Time;
if(%client.ndEquippedFromItem)
commandToClient(%client, 'setScrollMode', 2);
else
commandToClient(%client, 'setScrollMode', 3);
}
}
//Kill this mode
function NDM_FillColor::onKillMode(%this, %client)
{
//Destroy the selection
%client.ndSelection.delete();
//Remove the selection box
if(isObject(%client.ndSelectionBox))
%client.ndSelectionBox.delete();
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Plant Brick
function NDM_FillColor::onPlantBrick(%this, %client)
{
//Admin limit
if($Pref::Server::ND::PaintAdminOnly && !%client.isAdmin)
{
messageClient(%client, '', "\c6Paint Mode is admin only. Ask an admin for help.");
return;
}
//Normal colors
if(%client.currentFxColor $= "")
{
%client.ndSetMode(NDM_FillColorProgress);
%client.ndSelection.startFillColor(0, %client.currentColor);
return;
}
//Admin limit
if($Pref::Server::ND::PaintFxAdminOnly && !%client.isAdmin)
{
messageClient(%client, '', "\c6Paint Fx Mode is admin only. Ask an admin for help.");
return;
}
%client.ndSetMode(NDM_FillColorProgress);
if(%client.currentFxColor < 7)
%client.ndSelection.startFillColor(1, %client.currentFxColor);
else
%client.ndSelection.startFillColor(2, %client.currentFxColor - 7);
}
//Cancel Brick
function NDM_FillColor::onCancelBrick(%this, %client)
{
%client.ndSetMode(%client.ndLastSelectMode);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_FillColor::getBottomPrint(%this, %client)
{
%count = %client.ndSelection.brickCount;
%title = "Paint Mode (\c3" @ %count @ "\c6 Brick" @ (%count > 1 ? "s)" : ")");
if(%client.currentFxColor !$= "")
{
switch(%client.currentFxColor)
{
case 0: %color = "\c3Fx - None";
case 1: %color = "\c3Fx - Pearl";
case 2: %color = "\c3Fx - Chrome";
case 3: %color = "\c3Fx - Glow";
case 4: %color = "\c3Fx - Blink";
case 5: %color = "\c3Fx - Swirl";
case 6: %color = "\c3Fx - Rainbow";
case 7: %color = "\c3Fx - Stable";
case 8: %color = "\c3Fx - Undulo";
}
}
else
{
%color = "<font:impact:20>" @ ndGetPaintColorCode(%client.currentColor) @ "|||||<font:Verdana:16>\c3";
%alpha = mFloor(100 * getWord(getColorIdTable(%client.currentColor), 3));
if(%alpha != 100)
%color = %color SPC %alpha @ "%";
}
%l0 = "Select paint can to chose color";
%l1 = "Color: " @ %color;
%r0 = "[Plant Brick]: Paint bricks";
%r1 = "[Cancel Brick]: Exit mode";
return ndFormatMessage(%title, %l0, %r0, %l1, %r1, %l2);
}

View File

@ -0,0 +1,41 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Kill this mode
function NDM_FillColorProgress::onKillMode(%this, %client)
{
//Destroy the selection
%client.ndSelection.delete();
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Cancel Brick
function NDM_FillColorProgress::onCancelBrick(%this, %client)
{
%client.ndSelection.cancelFillColor();
%client.ndSetMode(NDM_FillColor);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_FillColorProgress::getBottomPrint(%this, %client)
{
%count = %client.ndSelection.brickCount;
%percent = mFloor(%client.ndSelection.paintIndex * 100 / %count);
%title = "Painting... (\c3" @ %percent @ "%\c6)";
%l0 = "[Cancel Brick]: Cancel painting";
return ndFormatMessage(%title, %l0);
}

View File

@ -0,0 +1,76 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Switch to this mode
function NDM_LoadProgress::onStartMode(%this, %client, %lastMode)
{
//Prepare selection to load data into
if(isObject(%client.ndSelection))
%client.ndSelection.deleteData();
else
%client.ndSelection = ND_Selection(%client);
}
//Kill this mode
function NDM_LoadProgress::onKillMode(%this, %client)
{
//Destroy selection
%client.ndSelection.delete();
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Cancel Brick
function NDM_LoadProgress::onCancelBrick(%this, %client)
{
%client.ndSelection.cancelLoading();
%client.ndSelection.delete();
%client.ndSetMode(%client.ndLastSelectMode);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_LoadProgress::getBottomPrint(%this, %client)
{
if(%client.ndSelection.loadStage == 0)
{
%count = %client.ndSelection.loadExpectedBrickCount;
if(%count != 0)
{
%percent = mFloor(%client.ndSelection.brickCount * 100 / %count);
%title = "Loading Bricks... (\c3" @ %percent @ "%\c6)";
}
else
%title = "Loading Bricks... (\c3" @ %client.ndSelection.brickCount @ "\c6 Bricks)";
}
else
{
%count = %client.ndSelection.loadExpectedConnectionCount;
if(%count != 0)
{
%percent = mFloor(%client.ndSelection.connectionCount * 100 / %count);
%title = "Loading Connections... (\c3" @ %percent @ "%\c6)";
}
else
%title = "Loading Connections... (\c3" @ %client.ndSelection.connectionCount @ "\c6 Connections)";
}
%l0 = "[Cancel Brick]: Cancel loading";
return ndFormatMessage(%title, %l0);
}

View File

@ -0,0 +1,268 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Switch to this mode
function NDM_PlantCopy::onStartMode(%this, %client, %lastMode)
{
if(%lastMode == $NDM::StackSelect
|| %lastMode == $NDM::BoxSelect
|| %lastMode == $NDM::CutProgress
|| %lastMode == $NDM::LoadProgress)
{
%client.ndSelection.spawnGhostBricks(%client.ndSelection.rootPosition, 0);
%client.ndSelection.angleIdReference = getAngleIDFromPlayer(%client.getControlObject());
}
%client.ndUpdateBottomPrint();
}
//Switch away from this mode
function NDM_PlantCopy::onChangeMode(%this, %client, %nextMode)
{
if(%nextMode == $NDM::StackSelect || %nextMode == $NDM::BoxSelect)
{
%client.ndSelection.deleteData();
}
}
//Kill this mode
function NDM_PlantCopy::onKillMode(%this, %client)
{
//Destroy the selection
%client.ndSelection.delete();
}
//Duplicator image callbacks
///////////////////////////////////////////////////////////////////////////
//Selecting an object with the duplicator
function NDM_PlantCopy::onSelectObject(%this, %client, %obj, %pos, %normal)
{
%this.moveBricksTo(%client, %pos, %normal);
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Prev Seat
function NDM_PlantCopy::onPrevSeat(%this, %client)
{
%client.ndPivot = !%client.ndPivot;
%client.ndUpdateBottomPrint();
if($Pref::Server::ND::PlayMenuSounds)
%client.play2d(%client.ndPivot ? lightOnSound : lightOffSound);
}
//Shift Brick
function NDM_PlantCopy::onShiftBrick(%this, %client, %x, %y, %z)
{
switch(getAngleIDFromPlayer(%client.getControlObject()))
{
case 0: %newX = %x; %newY = %y;
case 1: %newX = -%y; %newY = %x;
case 2: %newX = -%x; %newY = -%y;
case 3: %newX = %y; %newY = -%x;
}
%client.ndSelection.shiftGhostBricks(%newX / 2 SPC %newY / 2 SPC %z / 5);
}
//Super Shift Brick
function NDM_PlantCopy::onSuperShiftBrick(%this, %client, %x, %y, %z)
{
switch(getAngleIDFromPlayer(%client.getControlObject()))
{
case 0: %newX = %x; %newY = %y;
case 1: %newX = -%y; %newY = %x;
case 2: %newX = -%x; %newY = -%y;
case 3: %newX = %y; %newY = -%x;
}
if(%client.ndPivot)
%box = %client.ndSelection.getGhostWorldBox();
else
%box = %client.ndSelection.ghostGroup.getObject(0).getWorldBox();
%newX *= (getWord(%box, 3) - getWord(%box, 0));
%newy *= (getWord(%box, 4) - getWord(%box, 1));
%z *= (getWord(%box, 5) - getWord(%box, 2));
%client.ndSelection.shiftGhostBricks(%newX SPC %newY SPC %z);
}
//Rotate Brick
function NDM_PlantCopy::onRotateBrick(%this, %client, %direction)
{
%client.ndSelection.rotateGhostBricks(%direction, %client.ndPivot);
}
//Plant Brick
function NDM_PlantCopy::onPlantBrick(%this, %client)
{
//Check force plant
if(%client.ndForcePlant)
{
if($Pref::Server::ND::FloatAdminOnly && !%client.isAdmin)
{
messageClient(%client, '', "\c6Force Plant has been disabled because it is admin only. Ask an admin for help.");
%client.ndForcePlant = false;
}
}
%this.conditionalPlant(%client, %client.ndForcePlant);
}
//Cancel Brick
function NDM_PlantCopy::onCancelBrick(%this, %client)
{
if(%client.ndEquipped)
%client.ndSetMode(%client.ndLastSelectMode);
else
%client.ndKillMode();
}
//Paste Selection
function NDM_PlantCopy::onPaste(%this, %client)
{
%this.onPlantBrick(%client);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_PlantCopy::getBottomPrint(%this, %client)
{
%count = %client.ndSelection.brickCount;
%size = vectorSub(%client.ndSelection.maxSize, %client.ndSelection.minSize);
%x = mFloor(getWord(%size, 0) * 2);
%y = mFloor(getWord(%size, 1) * 2);
%z = mFloor(getWord(%size, 2) * 5);
if(%count == 1)
%title = "Plant Mode (\c31\c6 Brick)";
else if(%count <= $Pref::Server::ND::MaxGhostBricks)
%title = "Plant Mode (\c3" @ %count @ "\c6 Bricks)";
else
%title = "Plant Mode (\c3" @ %count @ "\c6 Bricks, \c3" @ mFloor($Pref::Server::ND::MaxGhostBricks * 100 / %count) @ "%\c6 Ghosted)";
%l0 = "Pivot: \c3" @ (%client.ndPivot ? "Whole Selection" : "Start Brick") @ "\c6 [Prev Seat]";
if(isObject(%client.ndSelection.targetGroup))
%l1 = "Planting as: \c3" @ %client.ndSelection.targetGroup.name;
else
%l1 = "Size: \c3" @ %x @ "\c6 x \c3" @ %y @ "\c6 x \c3" @ %z @ "\c6 Plates";
%r0 = "Use normal ghost brick controls";
%r1 = "[Cancel Brick] to exit plant mode";
return ndFormatMessage(%title, %l0, %r0, %l1, %r1);
}
//Functions
///////////////////////////////////////////////////////////////////////////
//Move the bricks to a specific location, like with the brick tool
function NDM_PlantCopy::moveBricksTo(%his, %client, %pos, %normal)
{
//Get half size of world box for offset
if(%client.ndPivot)
%box = %client.ndSelection.getGhostWorldBox();
else
%box = %client.ndSelection.ghostGroup.getObject(0).getWorldBox();
%halfSize = vectorScale(vectorSub(getWords(%box, 3, 5), getWords(%box, 0, 2)), 0.5);
//Point offset in correct direction based on normal
%offX = getWord(%halfSize, 0) * mFloatLength(getWord(%normal, 0), 0);
%offY = getWord(%halfSize, 1) * mFloatLength(getWord(%normal, 1), 0);
%offZ = getWord(%halfSize, 2) * mFloatLength(getWord(%normal, 2), 0);
%offset = %offX SPC %offY SPC %offZ;
//Get shift vector
%pos = vectorSub(vectorAdd(%pos, %offset), %client.ndSelection.ghostPosition);
if(%client.ndPivot)
{
%toCenter = %client.ndSelection.rootToCenter;
//Apply mirror
if(%client.ndSelection.ghostMirrorX)
%toCenter = -firstWord(%toCenter) SPC restWords(%toCenter);
else if(%client.ndSelection.ghostMirrorY)
%toCenter = getWord(%toCenter, 0) SPC -getWord(%toCenter, 1) SPC getWord(%toCenter, 2);
if(%client.ndSelection.ghostMirrorZ)
%toCenter = getWord(%toCenter, 0) SPC getWord(%toCenter, 1) SPC -getWord(%toCenter, 2);
%pos = vectorSub(%pos, ndRotateVector(%toCenter, %client.ndSelection.ghostAngleID));
}
%client.ndSelection.shiftGhostBricks(%pos);
//Offset required for New Brick Tool to display the tracer shape correctly
if(%client.ndPivot)
return vectorSub(%client.ndSelection.getGhostCenter(), %offset);
else
return vectorSub(%client.ndSelection.ghostGroup.getObject(0).getWorldBoxCenter(), %offset);
}
//Check time limit and attempt to plant bricks
function NDM_PlantCopy::conditionalPlant(%this, %client, %force)
{
//Check timeout
if(!%client.isAdmin && %client.ndLastPlantTime + ($Pref::Server::ND::PlantTimeoutMS / 1000) > $Sim::Time)
{
%remain = mCeil(%client.ndLastPlantTime + ($Pref::Server::ND::PlantTimeoutMS / 1000) - $Sim::Time);
if(%remain != 1)
%s = "s";
messageClient(%client, 'MsgError', "");
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6You need to wait\c3 " @ %remain @ "\c6 second" @ %s @ " before planting again!", 5);
return;
}
//Check too far distance
%offset = vectorSub(%client.ndSelection.getGhostCenter(), %client.getControlObject().position);
if(vectorLen(%offset) > $Pref::Server::TooFarDistance)
{
messageClient(%client, 'MsgError', "");
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6You can't plant so far away!", 5);
return;
}
//Validate target group
if(isObject(%client.ndSelection.targetGroup) &&
getTrustLevel(%client, %client.ndSelection.targetGroup) < 1 &&
(!%client.isAdmin || !$Pref::Server::ND::AdminTrustBypass2))
{
messageClient(%client, '', "\c6You need build trust with \c3"
@ %client.ndSelection.targetGroup.name @ "\c6 to plant bricks in their group.");
return;
}
%client.ndLastPlantTime = $Sim::Time;
%pos = %client.ndSelection.ghostPosition;
%ang = %client.ndSelection.ghostAngleID;
%client.ndSetMode(NDM_PlantCopyProgress);
%client.ndSelection.startPlant(%pos, %ang, %force);
}

View File

@ -0,0 +1,62 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Kill this mode
function NDM_PlantCopyProgress::onKillMode(%this, %client)
{
//Destroy the selection
%client.ndSelection.delete();
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Cancel Brick
function NDM_PlantCopyProgress::onCancelBrick(%this, %client)
{
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Planting canceled!", 4);
%client.ndSelection.cancelPlanting();
%client.ndSetMode(NDM_PlantCopy);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_PlantCopyProgress::getBottomPrint(%this, %client)
{
%qIndex = %client.ndSelection.plantQueueIndex;
%qCount = %client.ndSelection.plantQueueCount;
%count = %client.ndSelection.brickCount;
%planted = %client.ndSelection.plantSuccessCount;
if(%qIndex == %qCount)
{
//Searching for a brick
%pIndex = %client.ndSelection.plantSearchIndex;
%percent = mFloor(%client.ndSelection.plantSearchIndex * 100 / %count);
%title = "Finding Next Brick... (\c3" @ %percent @ "%\c6, \c3" @ %planted @ "\c6 planted)";
}
else
{
//Planting bricks
%failed = %client.ndSelection.plantTrustFailCount + %client.ndSelection.plantBlockedFailCount;
%percent = mFloor(%planted * 100 / %count);
%title = "Planting... (\c3" @ %percent @ "%\c6, \c3" @ %failed @ "\c6 failed)";
}
%l0 = "[Cancel Brick]: Cancel planting";
return ndFormatMessage(%title, %l0);
}

View File

@ -0,0 +1,43 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Kill this mode
function NDM_SaveProgress::onKillMode(%this, %client)
{
//Destroy selection
%client.ndSelection.delete();
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Cancel Brick
function NDM_SaveProgress::onCancelBrick(%this, %client)
{
%client.ndSelection.cancelSaving();
%client.ndSetMode(NDM_PlantCopy);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_SaveProgress::getBottomPrint(%this, %client)
{
%count = %client.ndSelection.brickCount;
%index = %client.ndSelection.saveIndex / 2 + %client.ndSelection.saveStage * %count / 2;
%percent = mFloor(%index * 100 / %count);
%title = "Saving Selection... (\c3" @ %percent @ "%\c6)";
%l0 = "[Cancel Brick]: Cancel saving";
return ndFormatMessage(%title, %l0);
}

View File

@ -0,0 +1,210 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Switch to this mode
function NDM_StackSelect::onStartMode(%this, %client, %lastMode)
{
%client.ndLastSelectMode = %this;
%client.ndUpdateBottomPrint();
}
//Switch away from this mode
function NDM_StackSelect::onChangeMode(%this, %client, %nextMode)
{
if(%nextMode == $NDM::FillColor
|| %nextMode == $NDM::PlantCopy
|| %nextMode == $NDM::WrenchProgress)
{
//Start de-highlighting the bricks
%client.ndSelection.deHighlight();
}
//The transition to box select mode will be
//handled in NDM_BoxSelect::onStartMode
}
//Kill this mode
function NDM_StackSelect::onKillMode(%this, %client)
{
//Destroy selection
if(isObject(%client.ndSelection))
%client.ndSelection.delete();
}
//Duplicator image callbacks
///////////////////////////////////////////////////////////////////////////
//Selecting an object with the duplicator
function NDM_StackSelect::onSelectObject(%this, %client, %obj, %pos, %normal)
{
if((%obj.getType() & $TypeMasks::FxBrickAlwaysObjectType) == 0)
return;
//Check timeout
if(!%client.isAdmin && %client.ndLastSelectTime + ($Pref::Server::ND::SelectTimeoutMS / 1000) > $Sim::Time)
{
%remain = mCeil(%client.ndLastSelectTime + ($Pref::Server::ND::SelectTimeoutMS / 1000) - $Sim::Time);
if(%remain != 1)
%s = "s";
messageClient(%client, 'MsgError', "");
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6You need to wait\c3 " @
%remain @ "\c6 second" @ %s @ " before selecting again!", 5);
return;
}
%client.ndLastSelectTime = $Sim::Time;
if(!ndTrustCheckMessage(%obj, %client))
return;
//Prepare selection to copy the bricks
if(!isObject(%client.ndSelection))
%client.ndSelection = ND_Selection(%client);
//Start selection
%client.ndSetMode(NDM_StackSelectProgress);
if(%client.ndMultiSelect)
%client.ndSelection.startStackSelectionAdditive(%obj, %client.ndDirection, %client.ndLimited);
else
%client.ndSelection.startStackSelection(%obj, %client.ndDirection, %client.ndLimited);
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Light key
function NDM_StackSelect::onLight(%this, %client)
{
if($Pref::Server::ND::PlayMenuSounds)
%client.play2d(lightOnSound);
%client.ndSetMode(NDM_BoxSelect);
}
//Next Seat
function NDM_StackSelect::onNextSeat(%this, %client)
{
%client.ndDirection = !%client.ndDirection;
%client.ndUpdateBottomPrint();
if($Pref::Server::ND::PlayMenuSounds)
%client.play2d(%client.ndDirection ? lightOnSound : lightOffSound);
}
//Prev Seat
function NDM_StackSelect::onPrevSeat(%this, %client)
{
%client.ndLimited = !%client.ndLimited;
%client.ndUpdateBottomPrint();
if($Pref::Server::ND::PlayMenuSounds)
%client.play2d(%client.ndLimited ? lightOnSound : lightOffSound);
}
//Shift Brick
function NDM_StackSelect::onShiftBrick(%this, %client, %x, %y, %z)
{
if(!isObject(%client.ndSelection) || !%client.ndSelection.brickCount)
return;
//Change to plant mode and apply the shift
%client.ndSetMode(NDM_PlantCopy);
NDM_PlantCopy.onShiftBrick(%client, %x, %y, %z);
}
//Super Shift Brick
function NDM_StackSelect::onSuperShiftBrick(%this, %client, %x, %y, %z)
{
if(!isObject(%client.ndSelection) || !%client.ndSelection.brickCount)
return;
//Change to plant mode and apply the shift
%client.ndSetMode(NDM_PlantCopy);
NDM_PlantCopy.onSuperShiftBrick(%client, %x, %y, %z);
}
//Rotate Brick
function NDM_StackSelect::onRotateBrick(%this, %client, %dir)
{
if(!isObject(%client.ndSelection) || !%client.ndSelection.brickCount)
return;
//Change to plant mode and apply the shift
%client.ndSetMode(NDM_PlantCopy);
NDM_PlantCopy.onRotateBrick(%client, %dir);
}
//Plant Brick
function NDM_StackSelect::onPlantBrick(%this, %client)
{
if(!isObject(%client.ndSelection) || !%client.ndSelection.brickCount)
return;
%client.ndSetMode(NDM_PlantCopy);
}
//Cancel Brick
function NDM_StackSelect::onCancelBrick(%this, %client)
{
if(isObject(%client.ndSelection))
%client.ndSelection.deleteData();
%client.ndUpdateBottomPrint();
}
//Copy Selection
function NDM_StackSelect::onCopy(%this, %client)
{
%this.onPlantBrick(%client);
}
//Cut Selection
function NDM_StackSelect::onCut(%this, %client)
{
if(!isObject(%client.ndSelection) || !%client.ndSelection.brickCount)
return;
%client.ndSetMode(NDM_CutProgress);
%client.ndSelection.startCutting();
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_StackSelect::getBottomPrint(%this, %client)
{
if(!isObject(%client.ndSelection) || !%client.ndSelection.brickCount)
{
%title = "Selection Mode";
%r0 = "Click Brick: Select stack " @ (%client.ndDirection ? "up" : "down");
%r1 = "";
}
else
{
%count = %client.ndSelection.brickCount;
%title = "Selection Mode (\c3" @ %count @ "\c6 Brick" @ (%count > 1 ? "s)" : ")");
%r0 = "Ctrl-Click Brick: Multiselect";
%r1 = "[Plant Brick]: Duplicate";
}
%l0 = "Type: \c3" @ (%client.ndMultiSelect ? "Multi-" : "") @ "Stack \c6[Light]";
%l1 = "Limited: " @ (%client.ndLimited ? "\c3Yes" : "\c0No") @ " \c6[Prev Seat]";
%l2 = "Direction: \c3" @ (%client.ndDirection ? "Up" : "Down") @ " \c6[Next Seat]";
return ndFormatMessage(%title, %l0, %r0, %l1, %r1, %l2);
}

View File

@ -0,0 +1,43 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Kill this mode
function NDM_StackSelectProgress::onKillMode(%this, %client)
{
//Destroy the selection
%client.ndSelection.delete();
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Cancel Brick
function NDM_StackSelectProgress::onCancelBrick(%this, %client)
{
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Selection canceled!", 4);
%client.ndSelection.cancelStackSelection();
%client.ndSetMode(NDM_StackSelect);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_StackSelectProgress::getBottomPrint(%this, %client)
{
%count = %client.ndSelection.brickCount;
%qCount = %client.ndSelection.queueCount - %count;
%title = "Selecting... (\c3" @ %count @ "\c6 Bricks, \c3" @ %qCount @ "\c6 in Queue)";
%l0 = "[Cancel Brick]: Cancel selection";
return ndFormatMessage(%title, %l0);
}

View File

@ -0,0 +1,50 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Kill this mode
function NDM_SuperCutProgress::onKillMode(%this, %client)
{
//Destroy the selection
%client.ndSelection.delete();
//Remove selection box
%client.ndSelectionBox.delete();
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Cancel Brick
function NDM_SuperCutProgress::onCancelBrick(%this, %client)
{
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Supercut canceled!", 4);
%client.ndSelection.cancelSuperCut();
%client.ndSetMode(NDM_BoxSelect);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_SuperCutProgress::getBottomPrint(%this, %client)
{
%curr = %client.ndSelection.currChunk + 1;
%num = %client.ndSelection.numChunks;
%percent = mFloor(%curr * 100 / %num);
%deleted = %client.ndSelection.superCutCount;
%planted = %client.ndSelection.superCutPlacedCount;
%title = "Supercut in progress... (\c3" @ %percent @ "%\c6, \c3" @ %deleted @ "\c6 deleted, \c3" @ %planted @ "\c6 planted)";
%l0 = "[Cancel Brick]: Cancel supercut";
return ndFormatMessage(%title, %l0);
}

View File

@ -0,0 +1,45 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Changing modes
///////////////////////////////////////////////////////////////////////////
//Kill this mode
function NDM_WrenchProgress::onKillMode(%this, %client)
{
//Destroy the selection
%client.ndSelection.delete();
//Remove the selection box
if(isObject(%client.ndSelectionBox))
%client.ndSelectionBox.delete();
}
//Generic inputs
///////////////////////////////////////////////////////////////////////////
//Cancel Brick
function NDM_WrenchProgress::onCancelBrick(%this, %client)
{
%client.ndSelection.cancelFillWrench();
%client.ndSetMode(%client.ndLastSelectMode);
}
//Interface
///////////////////////////////////////////////////////////////////////////
//Create bottomprint for client
function NDM_WrenchProgress::getBottomPrint(%this, %client)
{
%count = %client.ndSelection.brickCount;
%percent = mFloor(%client.ndSelection.wrenchIndex * 100 / %count);
%title = "Applying... (\c3" @ %percent @ "%\c6)";
%l0 = "[Cancel Brick]: Cancel";
return ndFormatMessage(%title, %l0);
}

View File

@ -0,0 +1,38 @@
// Deletes large numbers of ghost bricks without causing lag.
// -------------------------------------------------------------------
//Create a new ghost group
function ND_GhostGroup()
{
ND_ServerGroup.add(
%this = new ScriptGroup(ND_GhostGroup)
);
return %this;
}
//Delete some of the bricks in this group
function ND_GhostGroup::tickDelete(%this)
{
%max = $Pref::Server::ND::ProcessPerTick;
%bricks = getBrickCount();
//Deleting objects causes increasing lag with more bricks in total
if(%bricks > 450000)
%max /= 6;
else if(%bricks > 300000)
%max /= 4;
else if(%bricks > 150000)
%max /= 2;
if(%this.getCount() <= %max)
{
%this.delete();
return;
}
for(%i = 0; %i < %max; %i++)
%this.getObject(0).delete();
%this.schedule(30, tickDelete);
}

View File

@ -0,0 +1,112 @@
// Resizable highlight box to visualize the size of the selection.
// -------------------------------------------------------------------
//Create a new highlight box
function ND_HighlightBox()
{
ND_ServerGroup.add(
%this = new ScriptObject(ND_HighlightBox)
);
for(%i = 0; %i < 4; %i++)
{
%this.border_x[%i] = new StaticShape(){datablock = ND_SelectionBoxBorder;};
%this.border_y[%i] = new StaticShape(){datablock = ND_SelectionBoxBorder;};
%this.border_z[%i] = new StaticShape(){datablock = ND_SelectionBoxBorder;};
%this.border_x[%i].setScopeAlways();
%this.border_y[%i].setScopeAlways();
%this.border_z[%i].setScopeAlways();
}
%this.color = "1 0.84 0 0.99";
%this.applyColors();
return %this;
}
//Destroy static shapes when highlight box is removed
function ND_HighlightBox::onRemove(%this)
{
for(%i = 0; %i < 4; %i++)
{
%this.border_x[%i].delete();
%this.border_y[%i].delete();
%this.border_z[%i].delete();
}
}
//Apply color changes to the highlight box
function ND_HighlightBox::applyColors(%this)
{
for(%i = 0; %i < 4; %i++)
{
%this.border_x[%i].setNodeColor("ALL", %this.color);
%this.border_y[%i].setNodeColor("ALL", %this.color);
%this.border_z[%i].setNodeColor("ALL", %this.color);
}
}
//Return current size of highlight box
function ND_HighlightBox::getSize(%this)
{
return %this.point1 SPC %this.point2;
}
//Resize the highlight box
function ND_HighlightBox::setSize(%this, %point1, %point2)
{
if(getWordCount(%point1) == 6)
{
%point2 = getWords(%point1, 3, 5);
%point1 = getWords(%point1, 0, 2);
}
%this.point1 = %point1;
%this.point2 = %point2;
%x1 = getWord(%point1, 0);
%y1 = getWord(%point1, 1);
%z1 = getWord(%point1, 2);
%x2 = getWord(%point2, 0);
%y2 = getWord(%point2, 1);
%z2 = getWord(%point2, 2);
%len_x = %x2 - %x1;
%len_y = %y2 - %y1;
%len_z = %z2 - %z1;
%center_x = (%x1 + %x2) / 2;
%center_y = (%y1 + %y2) / 2;
%center_z = (%z1 + %z2) / 2;
%rot_x = "0 1 0 1.57079";
%rot_y = "1 0 0 1.57079";
%rot_z = "0 0 1 0";
%this.border_x0.setTransform(%center_x SPC %y1 SPC %z1 SPC %rot_x);
%this.border_x1.setTransform(%center_x SPC %y2 SPC %z1 SPC %rot_x);
%this.border_x2.setTransform(%center_x SPC %y2 SPC %z2 SPC %rot_x);
%this.border_x3.setTransform(%center_x SPC %y1 SPC %z2 SPC %rot_x);
%this.border_y0.setTransform(%x1 SPC %center_y SPC %z1 SPC %rot_y);
%this.border_y1.setTransform(%x2 SPC %center_y SPC %z1 SPC %rot_y);
%this.border_y2.setTransform(%x2 SPC %center_y SPC %z2 SPC %rot_y);
%this.border_y3.setTransform(%x1 SPC %center_y SPC %z2 SPC %rot_y);
%this.border_z0.setTransform(%x1 SPC %y1 SPC %center_z SPC %rot_z);
%this.border_z1.setTransform(%x2 SPC %y1 SPC %center_z SPC %rot_z);
%this.border_z2.setTransform(%x2 SPC %y2 SPC %center_z SPC %rot_z);
%this.border_z3.setTransform(%x1 SPC %y2 SPC %center_z SPC %rot_z);
%maxLen = getMax(getMax(%len_x, %len_y), %len_z);
%width = (7 / 1280) * %maxLen + 1;
for(%i = 0; %i < 4; %i++)
{
%this.border_x[%i].setScale(%width SPC %width SPC %len_x + %width * 0.05);
%this.border_y[%i].setScale(%width SPC %width SPC %len_y + %width * 0.05);
%this.border_z[%i].setScale(%width SPC %width SPC %len_z + %width * 0.05);
}
}

4494
classes/server/selection.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,496 @@
// Resizeable box to select all bricks inside a volume.
// -------------------------------------------------------------------
//Create a new selection box
function ND_SelectionBox(%shapeName)
{
ND_ServerGroup.add(
%this = new ScriptObject(ND_SelectionBox)
);
%this.innerBox = new StaticShape(){datablock = ND_SelectionBoxInner;};
%this.outerBox = new StaticShape(){datablock = ND_SelectionBoxOuter;};
%this.shapeName = new StaticShape(){datablock = ND_SelectionBoxShapeName;};
%this.corner1 = new StaticShape(){datablock = ND_SelectionBoxOuter;};
%this.corner2 = new StaticShape(){datablock = ND_SelectionBoxOuter;};
%this.selectedCorner = true;
%this.innerBox.setScopeAlways();
%this.outerBox.setScopeAlways();
%this.shapeName.setScopeAlways();
%this.corner1.setScopeAlways();
%this.corner2.setScopeAlways();
for(%i = 0; %i < 4; %i++)
{
%this.border_x[%i] = new StaticShape(){datablock = ND_SelectionBoxBorder;};
%this.border_y[%i] = new StaticShape(){datablock = ND_SelectionBoxBorder;};
%this.border_z[%i] = new StaticShape(){datablock = ND_SelectionBoxBorder;};
%this.border_x[%i].setScopeAlways();
%this.border_y[%i].setScopeAlways();
%this.border_z[%i].setScopeAlways();
}
%this.boxName = %shapeName;
%this.setNormalMode();
return %this;
}
//Destroy static shapes when selection box is removed
function ND_SelectionBox::onRemove(%this)
{
%this.innerBox.delete();
%this.outerBox.delete();
%this.shapeName.delete();
%this.corner1.delete();
%this.corner2.delete();
for(%i = 0; %i < 4; %i++)
{
%this.border_x[%i].delete();
%this.border_y[%i].delete();
%this.border_z[%i].delete();
}
}
//Set normal color values and borders
function ND_SelectionBox::setNormalMode(%this)
{
%this.innerColor = "0 0 0 0.60";
%this.outerColor = "0 0 0 0.35";
%this.borderColor = "1 0.84 0 0.99";
%this.borderColorSelected = "0 0 1 0.99";
%this.cornerColor1 = "0.8 0.74 0 0.99";
%this.cornerColor2 = "1 0.94 0.1 0.99";
%this.cornerColorSelected1 = "0 0.2 1 0.99";
%this.cornerColorSelected2 = "0 0.1 0.9 0.99";
%this.isNormalMode = true;
//Unhide the corners and inner/outer box (hidden in disabled mode)
%this.innerBox.unHideNode("ALL");
%this.corner1.unHideNode("ALL");
%this.corner2.unHideNode("ALL");
if(%this.hasVolume())
%this.outerBox.unHideNode("ALL");
//Apply changes
%this.applyColors();
%this.setSize(%this.point1, %this.point2);
%this.shapeName.setShapeName(%this.boxName);
}
//Set grayscale color values and slightly smaller border
function ND_SelectionBox::setDisabledMode(%this)
{
%this.borderColor = "0.1 0.1 0.1 0.4";
%this.borderColorSelected = "0.1 0.1 0.1 0.4";
%this.isNormalMode = false;
//Hide the corners and inner/outer box (looks better)
%this.innerBox.hideNode("ALL");
%this.outerBox.hideNode("ALL");
%this.corner1.hideNode("ALL");
%this.corner2.hideNode("ALL");
//Apply changes
%this.applyColors();
%this.setSize(%this.point1, %this.point2);
%this.shapeName.setShapeName("");
}
//Apply color changes to the selection box
function ND_SelectionBox::applyColors(%this)
{
%this.innerBox.setNodeColor("ALL", %this.innerColor);
%this.outerBox.setNodeColor("ALL", %this.outerColor);
%this.shapeName.setShapeNameColor(%this.borderColor);
for(%i = 0; %i < 4; %i++)
{
%this.border_x[%i].setNodeColor("ALL", %this.borderColor);
%this.border_y[%i].setNodeColor("ALL", %this.borderColor);
%this.border_z[%i].setNodeColor("ALL", %this.borderColor);
}
%bColor = %this.borderColorSelected;
if(%this.selectedCorner)
{
%this.border_x2.setNodeColor("ALL", %bColor);
%this.border_y2.setNodeColor("ALL", %bColor);
%this.border_z2.setNodeColor("ALL", %bColor);
%corner1 = %this.corner1;
%corner2 = %this.corner2;
}
else
{
%this.border_x0.setNodeColor("ALL", %bColor);
%this.border_y0.setNodeColor("ALL", %bColor);
%this.border_z0.setNodeColor("ALL", %bColor);
%corner1 = %this.corner2;
%corner2 = %this.corner1;
}
%corner1.setNodeColor("out+X", %this.borderColor);
%corner1.setNodeColor("out-X", %this.borderColor);
%corner1.setNodeColor("out+Y", %this.cornerColor1);
%corner1.setNodeColor("out-Y", %this.cornerColor1);
%corner1.setNodeColor("out+Z", %this.cornerColor2);
%corner1.setNodeColor("out-Z", %this.cornerColor2);
//Illusion of shaded box
%corner2.setNodeColor("out+X", %this.borderColorSelected);
%corner2.setNodeColor("out-X", %this.borderColorSelected);
%corner2.setNodeColor("out+Y", %this.cornerColorSelected1);
%corner2.setNodeColor("out-Y", %this.cornerColorSelected1);
%corner2.setNodeColor("out+Z", %this.cornerColorSelected2);
%corner2.setNodeColor("out-Z", %this.cornerColorSelected2);
}
//Return current size of selection box
function ND_SelectionBox::getSize(%this)
{
%x1 = getWord(%this.point1, 0);
%y1 = getWord(%this.point1, 1);
%z1 = getWord(%this.point1, 2);
%x2 = getWord(%this.point2, 0);
%y2 = getWord(%this.point2, 1);
%z2 = getWord(%this.point2, 2);
%min = getMin(%x1, %x2) SPC getMin(%y1, %y2) SPC getMin(%z1, %z2);
%max = getMax(%x1, %x2) SPC getMax(%y1, %y2) SPC getMax(%z1, %z2);
return vectorSub(%max, %min);
}
//Return current world box of selection box
function ND_SelectionBox::getWorldBox(%this)
{
%x1 = getWord(%this.point1, 0);
%y1 = getWord(%this.point1, 1);
%z1 = getWord(%this.point1, 2);
%x2 = getWord(%this.point2, 0);
%y2 = getWord(%this.point2, 1);
%z2 = getWord(%this.point2, 2);
%min = getMin(%x1, %x2) SPC getMin(%y1, %y2) SPC getMin(%z1, %z2);
%max = getMax(%x1, %x2) SPC getMax(%y1, %y2) SPC getMax(%z1, %z2);
return %min SPC %max;
}
//Resize the selection box
function ND_SelectionBox::setSize(%this, %point1, %point2)
{
if(getWordCount(%point1) == 6)
{
%point2 = getWords(%point1, 3, 5);
%point1 = getWords(%point1, 0, 2);
}
%this.point1 = %point1;
%this.point2 = %point2;
%x1 = getWord(%point1, 0);
%y1 = getWord(%point1, 1);
%z1 = getWord(%point1, 2);
%x2 = getWord(%point2, 0);
%y2 = getWord(%point2, 1);
%z2 = getWord(%point2, 2);
%len_x = mAbs(%x2 - %x1);
%len_y = mAbs(%y2 - %y1);
%len_z = mAbs(%z2 - %z1);
%center_x = (%x1 + %x2) / 2;
%center_y = (%y1 + %y2) / 2;
%center_z = (%z1 + %z2) / 2;
%rot_x = "0 1 0 1.57079";
%rot_y = "1 0 0 1.57079";
%rot_z = "0 0 1 0";
%this.innerBox.setTransform(%center_x SPC %center_y SPC %center_z);
%this.outerBox.setTransform(%center_x SPC %center_y SPC %center_z);
%this.shapeName.setTransform(%center_X SPC %center_y SPC %z2);
%this.border_x0.setTransform(%center_x SPC %y1 SPC %z1 SPC %rot_x);
%this.border_x1.setTransform(%center_x SPC %y2 SPC %z1 SPC %rot_x);
%this.border_x2.setTransform(%center_x SPC %y2 SPC %z2 SPC %rot_x);
%this.border_x3.setTransform(%center_x SPC %y1 SPC %z2 SPC %rot_x);
%this.border_y0.setTransform(%x1 SPC %center_y SPC %z1 SPC %rot_y);
%this.border_y1.setTransform(%x2 SPC %center_y SPC %z1 SPC %rot_y);
%this.border_y2.setTransform(%x2 SPC %center_y SPC %z2 SPC %rot_y);
%this.border_y3.setTransform(%x1 SPC %center_y SPC %z2 SPC %rot_y);
%this.border_z0.setTransform(%x1 SPC %y1 SPC %center_z SPC %rot_z);
%this.border_z1.setTransform(%x2 SPC %y1 SPC %center_z SPC %rot_z);
%this.border_z2.setTransform(%x2 SPC %y2 SPC %center_z SPC %rot_z);
%this.border_z3.setTransform(%x1 SPC %y2 SPC %center_z SPC %rot_z);
%this.corner1.setTransform(%x1 SPC %y1 SPC %z1);
%this.corner2.setTransform(%x2 SPC %y2 SPC %z2);
%this.innerBox.setScale(%len_x - 0.02 SPC %len_y - 0.02 SPC %len_z - 0.02);
%this.outerBox.setScale(%len_x + 0.02 SPC %len_y + 0.02 SPC %len_z + 0.02);
if(%this.isNormalMode)
{
//Normal mode (box with two colored corners)
%maxLen = getMax(getMax(%len_x, %len_y), %len_z);
%width = (7/1024) * %maxLen + 1;
for(%i = 0; %i < 4; %i++)
{
%this.border_x[%i].setScale(%width SPC %width SPC %len_x + %width * 0.05);
%this.border_y[%i].setScale(%width SPC %width SPC %len_y + %width * 0.05);
%this.border_z[%i].setScale(%width SPC %width SPC %len_z + %width * 0.05);
}
if(%this.selectedCorner)
{
%width1 = %width;
%width2 = %width + 0.02;
}
else
{
%width1 = %width + 0.02;
%width2 = %width;
}
//The borders touching the two corners are thicker to prevent Z fighting
//with the highlight box if it covers the same area as the selection
%this.border_x0.setScale(%width1 SPC %width1 SPC %len_x - %width * 0.05);
%this.border_y0.setScale(%width1 SPC %width1 SPC %len_y - %width * 0.05);
%this.border_z0.setScale(%width1 SPC %width1 SPC %len_z - %width * 0.05);
%this.border_x2.setScale(%width2 SPC %width2 SPC %len_x - %width * 0.05);
%this.border_y2.setScale(%width2 SPC %width2 SPC %len_y - %width * 0.05);
%this.border_z2.setScale(%width2 SPC %width2 SPC %len_z - %width * 0.05);
//Corners scale with the border width
%cs1 = 0.35 * %width1;
%cs2 = 0.35 * %width2;
%this.corner1.setScale(%cs1 SPC %cs1 SPC %cs1);
%this.corner2.setScale(%cs2 SPC %cs2 SPC %cs2);
}
else
{
//Disabled mode (transparent greyscale box)
%maxLen = getMax(getMax(%len_x, %len_y), %len_z);
%width = (21/5120) * %maxLen + 1;
for(%i = 0; %i < 4; %i++)
{
//Horizontal borders are a bit shorter to prevent z fighting
%this.border_x[%i].setScale(%width SPC %width SPC %len_x - %width * 0.05);
%this.border_y[%i].setScale(%width SPC %width SPC %len_y - %width * 0.05);
%this.border_z[%i].setScale(%width SPC %width SPC %len_z + %width * 0.05);
}
}
}
//Resize the selection box and align it to a player
function ND_SelectionBox::setSizeAligned(%this, %point1, %point2, %player)
{
//Set the selection box to correct orientation
%x1 = getWord(%point1, 0);
%y1 = getWord(%point1, 1);
%z1 = getWord(%point1, 2);
%x2 = getWord(%point2, 0);
%y2 = getWord(%point2, 1);
%z2 = getWord(%point2, 2);
switch(getAngleIDFromPlayer(%player))
{
case 0:
%p1 = %x1 SPC %y2 SPC %z1;
%p2 = %x2 SPC %y1 SPC %z2;
case 1:
%p1 = %x1 SPC %y1 SPC %z1;
%p2 = %x2 SPC %y2 SPC %z2;
case 2:
%p1 = %x2 SPC %y1 SPC %z1;
%p2 = %x1 SPC %y2 SPC %z2;
case 3:
%p1 = %x2 SPC %y2 SPC %z1;
%p2 = %x1 SPC %y1 SPC %z2;
}
//Select first corner
if(!%this.selectedCorner)
{
%this.selectedCorner = true;
%this.applyColors();
}
%this.setSize(%p1, %p2);
}
//Select one of the two corners
function ND_SelectionBox::switchCorner(%this)
{
%this.selectedCorner = !%this.selectedCorner;
if(%this.selectedCorner)
serverPlay3d(BrickRotateSound, %this.point2);
else
serverPlay3d(BrickRotateSound, %this.point1);
%this.setSize(%this.point1, %this.point2);
%this.applyColors();
}
//Move the selected corner
function ND_SelectionBox::shiftCorner(%this, %offset, %limit)
{
%oldP1 = %this.point1;
%oldP2 = %this.point2;
%limitReached = false;
//Size of a plate in TU
%unit[0] = 0.5;
%unit[1] = 0.5;
%unit[2] = 0.2;
for(%dim = 0; %dim < 3; %dim++)
{
//Copy current
%point1[%dim] = getWord(%this.point1, %dim);
%point2[%dim] = getWord(%this.point2, %dim);
//Get the size of the box in the current axis after resizing
%size = getWord(%this.point2, %dim) - getWord(%this.point1, %dim);
if(%this.selectedCorner)
{
//Update point2
%size += getWord(%offset, %dim);
%point2[%dim] += getWord(%offset, %dim);
//Check limits
if(mAbs(%size) > %limit)
{
%limitReached = true;
%point2[%dim] -= %size - %limit * (mAbs(%size) / %size);
}
}
else
{
//Update point1
%size -= getWord(%offset, %dim);
%point1[%dim] += getWord(%offset, %dim);
//Check limits
if(mAbs(%size) > %limit)
{
%limitReached = true;
%point1[%dim] += %size - %limit * (mAbs(%size) / %size);
}
}
}
//Update corner positions
%point1 = %point1[0] SPC %point1[1] SPC %point1[2];
%point2 = %point2[0] SPC %point2[1] SPC %point2[2];
%this.setSize(%point1, %point2);
//Play sounds
if(%this.selectedCorner)
%soundPoint = %point2;
else
%soundPoint = %point1;
if(%point1 !$= %oldP1 || %point2 !$= %oldP2)
serverPlay3d(BrickMoveSound, %soundPoint);
else
serverPlay3d(errorSound, %soundPoint);
//Hide outer box on selection boxes without volume
if(%this.hasVolume())
%this.outerBox.unHideNode("ALL");
else
%this.outerBox.hideNode("ALL");
return %limitReached;
}
//Move the entire box
function ND_SelectionBox::shift(%this, %offset)
{
%this.point1 = vectorAdd(%this.point1, %offset);
%this.point2 = vectorAdd(%this.point2, %offset);
%this.setSize(%this.point1, %this.point2);
//Play sounds
if(%this.selectedCorner)
serverPlay3d(BrickMoveSound, %this.point1);
else
serverPlay3d(BrickMoveSound, %this.point2);
}
//Rotate the entire box
function ND_SelectionBox::rotate(%this, %direction)
{
%point1 = %this.point1;
%point2 = %this.point2;
%center = vectorScale(vectorAdd(%point1, %point2), 0.5);
%brickSizeX = mAbs(mFloatLength((getWord(%point2, 0) - getWord(%point1, 0)) * 2, 0));
%brickSizeY = mAbs(mFloatLength((getWord(%point2, 1) - getWord(%point1, 1)) * 2, 0));
%shiftCorrect = "0 0 0";
if((%brickSizeX % 2) != (%brickSizeY % 2))
{
if(%brickSizeX % 2)
%shiftCorrect = "-0.25 -0.25 0";
else
%shiftCorrect = "0.25 0.25 0";
}
%point1 = vectorAdd(ndRotateVector(vectorSub(%point1, %center), %direction), %center);
%point2 = vectorAdd(ndRotateVector(vectorSub(%point2, %center), %direction), %center);
%this.setSize(vectorAdd(%point1, %shiftCorrect), vectorAdd(%point2, %shiftCorrect));
//Play sounds
if(%this.selectedCorner)
serverPlay3d(BrickRotateSound, %this.point1);
else
serverPlay3d(BrickRotateSound, %this.point2);
}
//Check if the box has a volume
function ND_SelectionBox::hasVolume(%this)
{
if(mAbs(getWord(%this.point1, 0) - getWord(%this.point2, 0)) < 0.05
|| mAbs(getWord(%this.point1, 1) - getWord(%this.point2, 1)) < 0.05
|| mAbs(getWord(%this.point1, 2) - getWord(%this.point2, 2)) < 0.05)
return false;
return true;
}

View File

@ -0,0 +1,75 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Delete this undo group
function ND_UndoGroupPaint::onRemove(%this)
{
if(%this.brickCount)
deleteVariables("$NU" @ %this.client @ "_" @ %this @ "_*");
}
//Start undo paint
function ND_UndoGroupPaint::ndStartUndo(%this, %client)
{
%client.ndUndoInProgress = true;
%client.ndLastMessageTime = $Sim::Time;
%this.ndTickUndo(%this.paintType, 0, %client);
}
//Tick undo paint
function ND_UndoGroupPaint::ndTickUndo(%this, %mode, %start, %client)
{
%end = %start + $Pref::Server::ND::ProcessPerTick;
if(%end > %this.brickCount)
%end = %this.brickCount;
for(%i = %start; %i < %end; %i++)
{
%brick = $NU[%client, %this, "B", %i];
if(!isObject(%brick))
continue;
%colorID = $NU[%client, %this, "V", %i];
switch(%mode)
{
case 0:
//Check whether brick is highlighted
%brick.setColor(%colorID);
case 1:
//Check whether brick is highlighted
if($NDHN[%brick])
$NDHF[%brick] = %colorID;
else
%brick.setColorFx(%colorID);
case 2:
%brick.setShapeFx(%colorID);
}
}
//If undo is taking long, tell the client how far we get
if(%client.ndLastMessageTime + 0.1 < $Sim::Time)
{
%client.ndLastMessageTime = $Sim::Time;
%percent = mFloor(%end * 100 / %this.brickCount);
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Undo in progress...\n<font:Verdana:17>\c3" @ %percent @ "%\c6 finished.", 10);
}
if(%end >= %this.brickcount)
{
%this.delete();
%client.ndUndoInProgress = false;
if(%start != 0)
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Undo finished.", 2);
return;
}
%this.schedule(30, ndTickUndo, %mode, %end, %client);
}

View File

@ -0,0 +1,61 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Start undo bricks
function SimSet::ndStartUndo(%this, %client)
{
if(!%this.brickCount)
{
%this.delete();
return;
}
%client.ndUndoInProgress = true;
%client.ndLastMessageTime = $Sim::Time;
%this.ndTickUndo(%this.brickCount, %client);
}
//Tick undo bricks
function SimSet::ndTickUndo(%this, %count, %client)
{
if(%count > %this.getCount())
%start = %this.getCount();
else
%start = %count;
if(%start > $Pref::Server::ND::ProcessPerTick)
%end = %start - $Pref::Server::ND::ProcessPerTick;
else
%end = 0;
for(%i = %start - 1; %i >= %end; %i--)
{
%brick = %this.getObject(%i);
%brick.killBrick();
if(%start > 1024)
%brick.delete();
}
//If undo is taking long, tell the client how far we get
if(%client.ndLastMessageTime + 0.1 < $Sim::Time)
{
%client.ndLastMessageTime = $Sim::Time;
%percent = mFloor(100 - (%end * 100 / %this.brickCount));
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Undo in progress...\n<font:Verdana:17>\c3" @ %percent @ "%\c6 finished.", 10);
}
if(%end <= 0)
{
%this.delete();
%client.ndUndoInProgress = false;
if(%start != 0)
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Undo finished.", 2);
return;
}
%this.schedule(30, ndTickUndo, %end, %client);
}

View File

@ -0,0 +1,197 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Delete this undo group
function ND_UndoGroupWrench::onRemove(%this)
{
if(%this.brickCount)
deleteVariables("$NU" @ %this.client @ "_" @ %this @ "_*");
}
//Start undo wrench
function ND_UndoGroupWrench::ndStartUndo(%this, %client)
{
%client.ndUndoInProgress = true;
%client.ndLastMessageTime = $Sim::Time;
%this.ndTickUndo(0, %client);
}
//Tick undo wrench
function ND_UndoGroupWrench::ndTickUndo(%this, %start, %client)
{
%end = %start + $Pref::Server::ND::ProcessPerTick;
if(%end > %this.brickCount)
%end = %this.brickCount;
setCurrentQuotaObject(getQuotaObjectFromClient(%client));
%fillWrenchName = %this.fillWrenchName;
%fillWrenchLight = %this.fillWrenchLight;
%fillWrenchEmitter = %this.fillWrenchEmitter;
%fillWrenchEmitterDir = %this.fillWrenchEmitterDir;
%fillWrenchItem = %this.fillWrenchItem;
%fillWrenchItemPos = %this.fillWrenchItemPos;
%fillWrenchItemDir = %this.fillWrenchItemDir;
%fillWrenchItemTime = %this.fillWrenchItemTime;
%fillWrenchRaycasting = %this.fillWrenchRaycasting;
%fillWrenchCollision = %this.fillWrenchCollision;
%fillWrenchRendering = %this.fillWrenchRendering;
for(%i = %start; %i < %end; %i++)
{
%brick = $NU[%client, %this, "B", %i];
if(!isObject(%brick))
continue;
//Revert wrench settings
if(%fillWrenchName)
{
%curr = getSubStr(%brick.getName(), 1, 254);
%fillWrenchNameValue = $NU[%client, %this, "N", %i];
if(%curr !$= %fillWrenchNameValue)
%brick.setNTObjectName(%fillWrenchNameValue);
}
if(%fillWrenchLight)
{
if(%tmp = %brick.light | 0)
%curr = %tmp.getDatablock();
else
%curr = 0;
%fillWrenchLightValue = $NU[%client, %this, "LDB", %i];
if(%curr != %fillWrenchLightValue)
%brick.setLight(%fillWrenchLightValue);
}
if(%fillWrenchEmitter)
{
if(%tmp = %brick.emitter | 0)
%curr = %tmp.getEmitterDatablock();
else if(%tmp = %brick.oldEmitterDB | 0)
%curr = %tmp;
else
%curr = 0;
%fillWrenchEmitterValue = $NU[%client, %this, "EDB", %i];
if(%curr != %fillWrenchEmitterValue)
%brick.setEmitter(%fillWrenchEmitterValue);
}
if(%fillWrenchEmitterDir)
{
%curr = %brick.emitterDirection;
%fillWrenchEmitterDirValue = $NU[%client, %this, "EDIR", %i];
if(%curr != %fillWrenchEmitterDirValue)
%brick.setEmitterDirection(%fillWrenchEmitterDirValue);
}
if(%fillWrenchItem)
{
if(%tmp = %brick.item | 0)
%curr = %tmp.getDatablock();
else
%curr = 0;
%fillWrenchItemValue = $NU[%client, %this, "IDB", %i];
if(%curr != %fillWrenchItemValue)
%brick.setItem(%fillWrenchItemValue);
}
if(%fillWrenchItemPos)
{
%curr = %brick.itemPosition;
%fillWrenchItemPosValue = $NU[%client, %this, "IPOS", %i];
if(%curr != %fillWrenchItemPosValue)
%brick.setItemPosition(%fillWrenchItemPosValue);
}
if(%fillWrenchItemDir)
{
%curr = %brick.itemPosition;
%fillWrenchItemDirValue = $NU[%client, %this, "IDIR", %i];
if(%curr != %fillWrenchItemDirValue)
%brick.setItemDirection(%fillWrenchItemDirValue);
}
if(%fillWrenchItemTime)
{
%curr = %brick.itemRespawnTime;
%fillWrenchItemTimeValue = $NU[%client, %this, "IRT", %i];
if(%curr != %fillWrenchItemTimeValue)
%brick.setItemRespawnTime(%fillWrenchItemTimeValue);
}
if(%fillWrenchRaycasting)
{
%curr = %brick.isRaycasting();
%fillWrenchRaycastingValue = $NU[%client, %this, "RC", %i];
if(%curr != %fillWrenchRaycastingValue)
%brick.setRaycasting(%fillWrenchRaycastingValue);
}
if(%fillWrenchCollision)
{
%curr = %brick.isColliding();
%fillWrenchCollisionValue = $NU[%client, %this, "C", %i];
if(%curr != %fillWrenchCollisionValue)
%brick.setColliding(%fillWrenchCollisionValue);
}
if(%fillWrenchRendering)
{
%curr = %brick.isRendering();
%fillWrenchRenderingValue = $NU[%client, %this, "R", %i];
if(%curr != %fillWrenchRenderingValue)
{
//Copy emitter ...?
if(!%fillWrenchRenderingValue && (%tmp = %brick.emitter | 0))
%emitter = %tmp.getEmitterDatablock();
else
%emitter = 0;
%brick.setRendering(%fillWrenchRenderingValue);
if(!%fillWrenchRenderingValue && %emitter)
%brick.setEmitter(%emitter);
}
}
}
clearCurrentQuotaObject();
//If undo is taking long, tell the client how far we get
if(%client.ndLastMessageTime + 0.1 < $Sim::Time)
{
%client.ndLastMessageTime = $Sim::Time;
%percent = mFloor(%end * 100 / %this.brickCount);
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Undo in progress...\n<font:Verdana:17>\c3" @ %percent @ "%\c6 finished.", 10);
}
if(%end >= %this.brickcount)
{
%this.delete();
%client.ndUndoInProgress = false;
if(%start != 0)
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Undo finished.", 2);
return;
}
%this.schedule(30, ndTickUndo, %end, %client);
}

4
description.txt Normal file
View File

@ -0,0 +1,4 @@
Title: New Duplicator
Author: Zeblote (1163)
New lag-free duplicator with intelligent selection modes
Redo's mod v1: better dupsave listing and search, fill and supercut logic wires, V20 support, possibly some other stuff I forgot about

BIN
resources/server/black.png Normal file

Binary file not shown.

BIN
resources/server/blank.png Normal file

Binary file not shown.

BIN
resources/server/blue.png Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
resources/server/icon.png Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
resources/server/white.png Normal file

Binary file not shown.

225
scripts/common/bytetable.cs Normal file
View File

@ -0,0 +1,225 @@
// Converts integers to characters and back. Mainly used by save transfer.
// -------------------------------------------------------------------
//Binary compression (file version, 241 allowed characters)
///////////////////////////////////////////////////////////////////////////
//Creates byte lookup table
function ndCreateByte241Table()
{
$ND::Byte241Lookup = "";
//This will map uints 0-241 to chars 15-255, starting after \r
for(%i = 15; %i < 256; %i++)
{
%char = collapseEscape("\\x" @
getSubStr("0123456789abcdef", (%i & 0xf0) >> 4, 1) @
getSubStr("0123456789abcdef", %i & 0x0f, 1));
$ND::Byte241ToChar[%i - 15] = %char;
$ND::Byte241Lookup = $ND::Byte241Lookup @ %char;
}
$ND::Byte241TableCreated = true;
}
//Packs uint in single byte
function ndPack241_1(%num)
{
return $ND::Byte241ToChar[%num];
}
//Packs uint in two bytes
function ndPack241_2(%num)
{
return $ND::Byte241ToChar[(%num / 241) | 0] @ $ND::Byte241ToChar[%num % 241];
}
//Packs uint in three bytes
function ndPack241_3(%num)
{
return
$ND::Byte241ToChar[(((%num / 241) | 0) / 241) | 0] @
$ND::Byte241ToChar[((%num / 241) | 0) % 241] @
$ND::Byte241ToChar[%num % 241];
}
//Packs uint in four bytes
function ndPack241_4(%num)
{
return
$ND::Byte241ToChar[(((((%num / 241) | 0) / 241) | 0) / 241) | 0] @
$ND::Byte241ToChar[((((%num / 241) | 0) / 241) | 0) % 241] @
$ND::Byte241ToChar[((%num / 241) | 0) % 241] @
$ND::Byte241ToChar[%num % 241];
}
//Unpacks uint from single byte
function ndUnpack241_1(%subStr)
{
return strStr($ND::Byte241Lookup, %subStr);
}
//Unpacks uint from two bytes
function ndUnpack241_2(%subStr)
{
return
strStr($ND::Byte241Lookup, getSubStr(%subStr, 0, 1)) * 241 +
strStr($ND::Byte241Lookup, getSubStr(%subStr, 1, 1));
}
//Unpacks uint from three bytes
function ndUnpack241_3(%subStr)
{
return
((strStr($ND::Byte241Lookup, getSubStr(%subStr, 0, 1)) * 58081) | 0) +
strStr($ND::Byte241Lookup, getSubStr(%subStr, 1, 1)) * 241 +
strStr($ND::Byte241Lookup, getSubStr(%subStr, 2, 1));
}
//Unpacks uint from four bytes
function ndUnpack241_4(%subStr)
{
return
((strStr($ND::Byte241Lookup, getSubStr(%subStr, 0, 1)) * 13997521) | 0) +
((strStr($ND::Byte241Lookup, getSubStr(%subStr, 1, 1)) * 58081) | 0) +
strStr($ND::Byte241Lookup, getSubStr(%subStr, 2, 1)) * 241 +
strStr($ND::Byte241Lookup, getSubStr(%subStr, 3, 1));
}
//Binary compression (command version, 255 allowed characters)
///////////////////////////////////////////////////////////////////////////
//Creates byte lookup table
function ndCreateByte255Table()
{
$ND::Byte255Lookup = "";
//This will map uints 0-254 to chars 1-255, starting after \x00
for(%i = 1; %i < 256; %i++)
{
%char = collapseEscape("\\x" @
getSubStr("0123456789abcdef", (%i & 0xf0) >> 4, 1) @
getSubStr("0123456789abcdef", %i & 0x0f, 1));
$ND::Byte255ToChar[%i - 1] = %char;
$ND::Byte255Lookup = $ND::Byte255Lookup @ %char;
}
$ND::Byte255TableCreated = true;
}
//Packs uint in single byte
function ndPack255_1(%num)
{
return $ND::Byte255ToChar[%num];
}
//Packs uint in two bytes
function ndPack255_2(%num)
{
return $ND::Byte255ToChar[(%num / 255) | 0] @ $ND::Byte255ToChar[%num % 255];
}
//Packs uint in three bytes
function ndPack255_3(%num)
{
return
$ND::Byte255ToChar[(((%num / 255) | 0) / 255) | 0] @
$ND::Byte255ToChar[((%num / 255) | 0) % 255] @
$ND::Byte255ToChar[%num % 255];
}
//Packs uint in four bytes
function ndPack255_4(%num)
{
return
$ND::Byte255ToChar[(((((%num / 255) | 0) / 255) | 0) / 255) | 0] @
$ND::Byte255ToChar[((((%num / 255) | 0) / 255) | 0) % 255] @
$ND::Byte255ToChar[((%num / 255) | 0) % 255] @
$ND::Byte255ToChar[%num % 255];
}
//Unpacks uint from single byte
function ndUnpack255_1(%subStr)
{
return strStr($ND::Byte255Lookup, %subStr);
}
//Unpacks uint from two bytes
function ndUnpack255_2(%subStr)
{
return
strStr($ND::Byte255Lookup, getSubStr(%subStr, 0, 1)) * 255 +
strStr($ND::Byte255Lookup, getSubStr(%subStr, 1, 1));
}
//Unpacks uint from three bytes
function ndUnpack255_3(%subStr)
{
return
((strStr($ND::Byte255Lookup, getSubStr(%subStr, 0, 1)) * 65025) | 0) +
strStr($ND::Byte255Lookup, getSubStr(%subStr, 1, 1)) * 255 +
strStr($ND::Byte255Lookup, getSubStr(%subStr, 2, 1)) | 0;
}
//Unpacks uint from four bytes
function ndUnpack255_4(%subStr)
{
return
((strStr($ND::Byte255Lookup, getSubStr(%subStr, 0, 1)) * 16581375) | 0) +
((strStr($ND::Byte255Lookup, getSubStr(%subStr, 1, 1)) * 65025) | 0) +
strStr($ND::Byte255Lookup, getSubStr(%subStr, 2, 1)) * 255 +
strStr($ND::Byte255Lookup, getSubStr(%subStr, 3, 1)) | 0;
}
//Some tests for the packing functions
function ndTestPack255()
{
echo("Testing 1 byte");
echo(ndUnpack255_1(ndPack255_1(0)) == 0);
echo(ndUnpack255_1(ndPack255_1(123)) == 123);
echo(ndUnpack255_1(ndPack255_1(231)) == 231);
echo(ndUnpack255_1(ndPack255_1(254)) == 254);
echo("Testing 2 byte");
echo(ndUnpack255_2(ndPack255_2(0)) == 0);
echo(ndUnpack255_2(ndPack255_2(123)) == 123);
echo(ndUnpack255_2(ndPack255_2(231)) == 231);
echo(ndUnpack255_2(ndPack255_2(254)) == 254);
echo(ndUnpack255_2(ndPack255_2(12345)) == 12345);
echo(ndUnpack255_2(ndPack255_2(32145)) == 32145);
echo(ndUnpack255_2(ndPack255_2(65024)) == 65024);
echo("Testing 3 byte");
echo(ndUnpack255_3(ndPack255_3(0)) == 0);
echo(ndUnpack255_3(ndPack255_3(123)) == 123);
echo(ndUnpack255_3(ndPack255_3(231)) == 231);
echo(ndUnpack255_3(ndPack255_3(254)) == 254);
echo(ndUnpack255_3(ndPack255_3(12345)) == 12345);
echo(ndUnpack255_3(ndPack255_3(32145)) == 32145);
echo(ndUnpack255_3(ndPack255_3(65024)) == 65024);
echo(ndUnpack255_3(ndPack255_3(11234567)) == 11234567);
echo(ndUnpack255_3(ndPack255_3(14132451)) == 14132451);
echo(ndUnpack255_3(ndPack255_3(16581374)) == 16581374);
echo("Testing 4 byte");
echo(ndUnpack255_4(ndPack255_4(0)) == 0);
echo(ndUnpack255_4(ndPack255_4(123)) == 123);
echo(ndUnpack255_4(ndPack255_4(231)) == 231);
echo(ndUnpack255_4(ndPack255_4(254)) == 254);
echo(ndUnpack255_4(ndPack255_4(12345)) == 12345);
echo(ndUnpack255_4(ndPack255_4(32145)) == 32145);
echo(ndUnpack255_4(ndPack255_4(65024)) == 65024);
echo(ndUnpack255_4(ndPack255_4(11234567)) == 11234567);
echo(ndUnpack255_4(ndPack255_4(14132451)) == 14132451);
echo(ndUnpack255_4(ndPack255_4(16581374)) == 16581374);
echo(ndUnpack255_4(ndPack255_4(1234567890)) == 1234567890);
//Appearantly tork uses uint and normal int randomly in
//seperate places so we can't use the full uint range
echo(ndUnpack255_4(ndPack255_4(2147483647)) == 2147483647);
echo(ndUnpack255_4(ndPack255_4(2147483648)) != 2147483648);
}

1151
scripts/server/commands.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,344 @@
// Creates datablocks for the handheld items and the selection box.
// -------------------------------------------------------------------
//Basic golden duplicator
///////////////////////////////////////////////////////////////////////////
//Duplicator Item
datablock ItemData(ND_Item)
{
cameraMaxDist = 0.1;
canDrop = 1;
category = "Tools";
className = "Weapon";
density = 0.2;
doColorShift = false;
colorShiftColor = "1 0.84 0 1";
elasticity = 0.2;
emap = 1;
friction = 0.6;
iconName = $ND::ResourcePath @ "server/icon";
image = "ND_Image";
shapeFile = $ND::ResourcePath @ "server/duplicator_brick.dts";
uiName = "Duplicator";
};
//Particles for explosion
datablock ParticleData(ND_HitParticle)
{
colors[0] = "1 0.84 0 0.9";
colors[1] = "1 0.84 0 0.7";
colors[2] = "1 0.84 0 0.5";
gravityCoefficient = 0.7;
lifetimeMS = 600;
lifetimeVarianceMS = 200;
sizes[0] = 0.6;
sizes[1] = 0.4;
sizes[2] = 0.3;
spinRandomMax = 90;
spinRandomMin = -90;
textureName = "base/client/ui/brickIcons/2x2";
times[1] = 0.8;
times[2] = 1;
};
//Emitter for explosion
datablock ParticleEmitterData(ND_HitEmitter)
{
lifetimeMS = 20;
ejectionPeriodMS = 1;
periodVarianceMS = 0;
ejectionVelocity = 3;
ejectionOffset = 0.2;
particles = ND_HitParticle;
thetaMin = 20;
thetaMax = 80;
velocityVariance = 0;
};
//Explosion
datablock ExplosionData(ND_HitExplosion)
{
camShakeDuration = 0.5;
camShakeFreq = "1 1 1";
emitter[0] = ND_HitEmitter;
faceViewer = 1;
lifetimeMS = 180;
lightEndRadius = 0;
lightStartColor = "0 0 0 0";
lightStartRadius = 0;
shakeCamera = 1;
soundProfile = "wandHitSound";
};
//Projectile to make explosion
datablock ProjectileData(ND_HitProjectile)
{
bounceElasticity = 0;
bounceFriction = 0;
explodeOnDeath = 1;
explosion = ND_HitExplosion;
fadeDelay = 2;
gravityMod = 0;
lifetime = 0;
range = 10;
};
//Swing particles
datablock ParticleData(ND_WaitParticle)
{
colors[0] = "1 0.84 0 0.9";
colors[1] = "1 0.84 0 0.7";
colors[2] = "1 0.84 0 0.5";
gravityCoefficient = -0.4;
dragCoefficient = 2;
lifetimeMS = 400;
lifetimeVarianceMS = 200;
sizes[0] = 0.5;
sizes[1] = 0.8;
sizes[2] = 0;
spinRandomMax = 0;
spinRandomMin = 0;
textureName = "base/client/ui/brickIcons/1x1";
times[1] = 0.5;
times[2] = 1;
};
//Swing emitter
datablock ParticleEmitterData(ND_WaitEmitter)
{
lifetimeMS = 5000;
ejectionPeriodMS = 10;
periodVarianceMS = 0;
ejectionVelocity = 1;
ejectionOffset = 0.01;
particles = ND_WaitParticle;
thetaMin = 20;
thetaMax = 80;
velocityVariance = 0;
};
//Spin particles
datablock ParticleData(ND_SpinParticle : ND_WaitParticle)
{
colors[0] = "1 0.65 0 0.9";
colors[1] = "1 0.65 0 0.7";
colors[2] = "1 0.65 0 0.5";
gravityCoefficient = 0;
sizes[0] = 0.3;
sizes[1] = 0.5;
sizes[2] = 0;
textureName = "base/client/ui/brickIcons/1x1";
};
//Spin emitter
datablock ParticleEmitterData(ND_SpinEmitter : ND_WaitEmitter)
{
particles = ND_SpinParticle;
ejectionPeriodMS = 15;
thetaMin = 40;
thetaMax = 140;
ejectionVelocity = 2;
};
//Duplicator image
datablock ShapeBaseImageData(ND_Image)
{
shapeFile = $ND::ResourcePath @ "server/duplicator_brick.dts";
className = "WeaponImage";
emap = true;
mountPoint = 0;
offset = "0 0 0";
eyeOffset = "0.7 1.4 -0.9";
armReady = true;
showBricks = true;
doColorShift = true;
colorShiftColor = "1 0.84 0 1";
item = ND_Item;
projectile = ND_HitProjectile;
loaded = false;
//Image states
stateName[0] = "Activate";
stateSpinThread[0] = "Stop";
stateTimeoutValue[0] = 0;
stateAllowImageChange[0] = false;
stateTransitionOnTimeout[0] = "Idle";
stateName[1] = "Idle";
stateSpinThread[1] = "Stop";
stateAllowImageChange[1] = true;
stateTransitionOnNotLoaded[1] = "StartSpinning";
stateTransitionOnTriggerDown[1] = "PreFire";
stateName[2] = "PreFire";
stateScript[2] = "onPreFire";
stateTimeoutValue[2] = 0.01;
stateAllowImageChange[2] = false;
stateTransitionOnTimeout[2] = "Fire";
stateName[3] = "Fire";
stateFire[3] = true;
stateScript[3] = "onFire";
stateEmitter[3] = ND_WaitEmitter;
stateSequence[3] = "swing";
stateEmitterNode[3] = "muzzlePoint";
stateEmitterTime[3] = 0.01;
stateTimeoutValue[3] = 0.01;
stateWaitForTimeout[3] = true;
stateAllowImageChange[3] = false;
stateTransitionOnTimeout[3] = "CheckFire";
stateName[4] = "CheckFire";
stateSpinThread[4] = "Stop";
stateTransitionOnNotLoaded[4] = "StartSpinning_TDown";
stateTransitionOnTriggerUp[4] = "Idle";
//Spinning states (from idle)
stateName[5] = "StartSpinning";
stateSpinThread[5] = "SpinUp";
stateTimeoutValue[5] = 0.25;
stateTransitionOnTimeout[5] = "IdleSpinning";
stateName[6] = "IdleSpinning";
stateEmitter[6] = ND_SpinEmitter;
stateSpinThread[6] = "FullSpeed";
stateEmitterNode[6] = "muzzlePoint";
stateEmitterTime[6] = 0.35;
stateTimeoutValue[6] = 0.35;
stateTransitionOnLoaded[6] = "StopSpinning";
stateTransitionOnTimeout[6] = "IdleSpinning";
stateName[7] = "StopSpinning";
stateSpinThread[7] = "SpinDown";
stateTimeoutValue[7] = 0.25;
stateTransitionOnTimeout[7] = "Idle";
//Spinning states (from checkfire, trigger is still down)
stateName[8] = "StartSpinning_TDown";
stateSpinThread[8] = "SpinUp";
stateTimeoutValue[8] = 0.25;
stateTransitionOnTimeout[8] = "IdleSpinning_TDown";
stateName[9] = "IdleSpinning_TDown";
stateEmitter[9] = ND_SpinEmitter;
stateSpinThread[9] = "FullSpeed";
stateEmitterNode[9] = "muzzlePoint_TDown";
stateEmitterTime[9] = 0.4;
stateTimeoutValue[9] = 0.4;
stateTransitionOnLoaded[9] = "StopSpinning_TDown";
stateTransitionOnTimeout[9] = "IdleSpinning_TDown";
stateName[10] = "StopSpinning_TDown";
stateSpinThread[10] = "SpinDown";
stateTimeoutValue[10] = 0.25;
stateTransitionOnTimeout[10] = "CheckFire";
};
//Spinning selection box for box mode
///////////////////////////////////////////////////////////////////////////
//Duplicator image
datablock ShapeBaseImageData(ND_Image_Box : ND_Image)
{
shapeFile = $ND::ResourcePath @ "server/duplicator_selection.dts";
};
//Blue duplicator for plant mode
///////////////////////////////////////////////////////////////////////////
//Particles for explosion
datablock ParticleData(ND_HitParticle_Blue : ND_HitParticle)
{
colors[0] = "0 0.25 1 0.9";
colors[1] = "0 0.25 1 0.7";
colors[2] = "0 0.25 1 0.5";
};
//Emitter for explosion
datablock ParticleEmitterData(ND_HitEmitter_Blue : ND_HitEmitter)
{
particles = ND_HitParticle_Blue;
};
//Explosion
datablock ExplosionData(ND_HitExplosion_Blue : ND_HitExplosion)
{
emitter[0] = ND_HitEmitter_Blue;
};
//Projectile to make explosion
datablock ProjectileData(ND_HitProjectile_Blue : ND_HitProjectile)
{
explosion = ND_HitExplosion_Blue;
};
//Swing particles
datablock ParticleData(ND_WaitParticle_Blue : ND_WaitParticle)
{
colors[0] = "0 0.25 1 0.9";
colors[1] = "0 0.25 1 0.7";
colors[2] = "0 0.25 1 0.5";
};
//Swing emitter
datablock ParticleEmitterData(ND_WaitEmitter_Blue : ND_WaitEmitter)
{
particles = ND_WaitParticle_Blue;
};
//Spin particles
datablock ParticleData(ND_SpinParticle_Blue : ND_SpinParticle)
{
colors[0] = "0 0.25 0.75 0.9";
colors[1] = "0 0.25 0.75 0.7";
colors[2] = "0 0.25 0.75 0.5";
};
//Spin emitter
datablock ParticleEmitterData(ND_SpinEmitter_Blue : ND_SpinEmitter)
{
particles = ND_SpinParticle_Blue;
};
//Duplicator image
datablock ShapeBaseImageData(ND_Image_Blue : ND_Image)
{
colorShiftColor = "0 0.25 1 1";
projectile = ND_HitProjectile_Blue;
//Image states
stateEmitter[3] = ND_WaitEmitter_Blue;
stateEmitter[6] = ND_SpinEmitter_Blue;
stateEmitter[9] = ND_SpinEmitter_Blue;
};
//Resizable selection and highlight box
///////////////////////////////////////////////////////////////////////////
//Transparent box to visualize bricks intersecting selection box
datablock StaticShapeData(ND_SelectionBoxOuter)
{
shapeFile = $ND::ResourcePath @ "server/selectionbox_outer.dts";
};
//Inside box (inverted normals) to visualize backfaces
datablock StaticShapeData(ND_SelectionBoxInner)
{
shapeFile = $ND::ResourcePath @ "server/selectionbox_inner.dts";
};
//Small box to create solid edges
datablock StaticShapeData(ND_SelectionBoxBorder)
{
shapeFile = $ND::ResourcePath @ "server/selectionbox_border.dts";
};
//Empty shape to hold shapename
datablock StaticShapeData(ND_SelectionBoxShapeName)
{
shapeFile = "base/data/shapes/empty.dts";
};

734
scripts/server/functions.cs Normal file
View File

@ -0,0 +1,734 @@
// This file should not exist. Fix later...
// -------------------------------------------------------------------
//Math functions
///////////////////////////////////////////////////////////////////////////
//Rotate vector around +Z in 90 degree steps
function ndRotateVector(%vector, %steps)
{
switch(%steps % 4)
{
case 0: return %vector;
case 1: return getWord(%vector, 1) SPC -getWord(%vector, 0) SPC getWord(%vector, 2);
case 2: return -getWord(%vector, 0) SPC -getWord(%vector, 1) SPC getWord(%vector, 2);
case 3: return -getWord(%vector, 1) SPC getWord(%vector, 0) SPC getWord(%vector, 2);
}
}
//Rotate and mirror a direction
function ndTransformDirection(%dir, %steps, %mirrX, %mirrY, %mirrZ)
{
if(%dir > 1)
{
if(%mirrX && %dir % 2 == 1
|| %mirrY && %dir % 2 == 0)
%dir += 2;
%dir = (%dir + %steps - 2) % 4 + 2;
}
else if(%mirrZ)
%dir = !%dir;
return %dir;
}
//Get the closest paint color to an rgb value
function ndGetClosestColorID(%rgb)
{
//Set initial value
%best = 0;
%bestDiff = 999999;
for(%i = 0; %i < 64; %i++)
{
%color = getColorI(getColorIdTable(%i));
%diff = vectorLen(vectorSub(%rgb, %color));
if(getWord(%color, 3) != 255)
%diff += 1000;
if(%diff < %bestDiff)
{
%best = %i;
%bestDiff = %diff;
}
}
return %best;
}
//Get the closest paint color to an rgba value
function ndGetClosestColorID2(%rgba)
{
%rgb = getWords(%rgba, 0, 2);
%a = getWord(%rgba, 3);
//Set initial value
%best = 0;
%bestDiff = 999999;
for(%i = 0; %i < 64; %i++)
{
%color = getColorI(getColorIdTable(%i));
%alpha = getWord(%color, 3);
%diff = vectorLen(vectorSub(%rgb, %color));
if((%alpha > 254) != (%a > 254))
%diff += 1000;
else
%diff += mAbs(%alpha - %a) * 0.5;
if(%diff < %bestDiff)
{
%best = %i;
%bestDiff = %diff;
}
}
return %best;
}
//Convert a paint color to a <color:xxxxxx> code
function ndGetPaintColorCode(%id)
{
%rgb = getColorI(getColorIdTable(%id));
%chars = "0123456789abcdef";
%r = getWord(%rgb, 0);
%g = getWord(%rgb, 1);
%b = getWord(%rgb, 2);
%r1 = getSubStr(%chars, (%r / 16) | 0, 1);
%r2 = getSubStr(%chars, %r % 16 , 1);
%g1 = getSubStr(%chars, (%g / 16) | 0, 1);
%g2 = getSubStr(%chars, %g % 16 , 1);
%b1 = getSubStr(%chars, (%b / 16) | 0, 1);
%b2 = getSubStr(%chars, %b % 16 , 1);
return "<color:" @ %r1 @ %r2 @ %g1 @ %g2 @ %b1 @ %b2 @ ">";
}
//Get a plate world box from a raycast
function ndGetPlateBoxFromRayCast(%pos, %normal)
{
//Get half size of world box for offset
%halfSize = "0.25 0.25 0.1";
//Point offset in correct direction based on normal
%offX = getWord(%halfSize, 0) * mFloatLength(-getWord(%normal, 0), 0);
%offY = getWord(%halfSize, 1) * mFloatLength(-getWord(%normal, 1), 0);
%offZ = getWord(%halfSize, 2) * mFloatLength(-getWord(%normal, 2), 0);
%offset = %offX SPC %offY SPC %offZ;
//Get offset position
%newPos = vectorAdd(%pos, %offset);
//Get the plate box around the position
%x1 = mFloor(getWord(%newPos, 0) * 2) / 2;
%y1 = mFloor(getWord(%newPos, 1) * 2) / 2;
%z1 = mFloor(getWord(%newPos, 2) * 5) / 5;
%x2 = mCeil(getWord(%newPos, 0) * 2) / 2;
%y2 = mCeil(getWord(%newPos, 1) * 2) / 2;
%z2 = mCeil(getWord(%newPos, 2) * 5) / 5;
return %x1 SPC %y1 SPC %z1 SPC %x2 SPC %y2 SPC %z2;
}
//Trust checks
///////////////////////////////////////////////////////////////////////////
//Send a message if a client doesn't have select trust to a brick
function ndTrustCheckMessage(%obj, %client)
{
%group = %client.brickGroup.getId();
%bl_id = %client.bl_id;
%admin = %client.isAdmin;
if(ndTrustCheckSelect(%obj, %group, %bl_id, %admin))
return true;
if(%obj.getGroup().bl_id == 888888 && !$Pref::Server::ND::SelectPublicBricks)
return false;
messageClient(%client, 'MsgError', "");
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6You don't have enough trust to do that!", 5);
return false;
}
//Check whether a client has enough trust to select a brick
function ndTrustCheckSelect(%obj, %group2, %bl_id, %admin)
{
%group1 = %obj.getGroup();
//Client owns brick
if(%group1 == %group2)
return true;
//Client owns stack
if(%obj.stackBL_ID == %bl_id)
return true;
//Client has trust to the brick
if(%group1.Trust[%bl_id] >= $Pref::Server::ND::TrustLimit)
return true;
//Client has trust to the stack of the brick
if(%group2.Trust[%obj.stackBL_ID] >= $Pref::Server::ND::TrustLimit)
return true;
//Client is admin
if(%admin && $Pref::Server::ND::AdminTrustBypass1)
return true;
//Client can duplicate public bricks
if(%group1.bl_id == 888888 && $Pref::Server::ND::SelectPublicBricks)
return true;
return false;
}
//Check whether a client has enough trust to modify a brick
function ndTrustCheckModify(%obj, %group2, %bl_id, %admin)
{
%group1 = %obj.getGroup();
//Client owns brick
if(%group1 == %group2)
return true;
//Client owns stack
if(%obj.stackBL_ID == %bl_id)
return true;
//Client has trust to the brick
if(%group1.Trust[%bl_id] >= 2)
return true;
//Client has trust to the stack of the brick
if(%group2.Trust[%obj.stackBL_ID] >= 2)
return true;
//Client is admin
if(%admin && $Pref::Server::ND::AdminTrustBypass2)
return true;
return false;
}
//Fast check whether a client has enough trust to plant on a brick
function ndFastTrustCheck(%brick, %bl_id, %brickGroup)
{
%group = %brick.getGroup();
if(%group == %brickGroup)
return true;
if(%group.Trust[%bl_id] > 0)
return true;
if(%group.bl_id == 888888)
return true;
return false;
}
//General stuff
///////////////////////////////////////////////////////////////////////////
//Setup list of spawned clients
function ndUpdateSpawnedClientList()
{
$ND::NumSpawnedClients = 0;
for(%i = 0; %i < ClientGroup.getCount(); %i++)
{
%cl = ClientGroup.getObject(%i);
if(%cl.hasSpawnedOnce)
{
$ND::SpawnedClient[$ND::NumSpawnedClients] = %cl;
$ND::NumSpawnedClients++;
}
}
}
//Applies mirror effect to a single ghost brick
function FxDtsBrick::ndMirrorGhost(%brick, %client, %axis)
{
//Offset position
%bPos = %brick.position;
//Rotated local angle id
%bAngle = %brick.angleID;
//Apply mirror effects (ugh)
%datablock = %brick.getDatablock();
if(%axis == 0)
{
//Handle symmetries
switch($ND::Symmetry[%datablock])
{
//Asymmetric
case 0:
if(%db = $ND::SymmetryXDatablock[%datablock])
{
%datablock = %db;
%bAngle = (%bAngle + $ND::SymmetryXOffset[%datablock]) % 4;
//Pair is made on X, so apply mirror logic for X afterwards
if(%bAngle % 2 == 1)
%bAngle = (%bAngle + 2) % 4;
}
else
{
messageClient(%client, '', "\c6Sorry, your ghost brick is asymmetric and cannot be mirrored.");
return;
}
//Do nothing for fully symmetric
//X symmetric - rotate 180 degrees if brick is angled 90 or 270 degrees
case 2:
if(%bAngle % 2 == 1)
%bAngle = (%bAngle + 2) % 4;
//Y symmetric - rotate 180 degrees if brick is angled 0 or 180 degrees
case 3:
if(%bAngle % 2 == 0)
%bAngle = (%bAngle + 2) % 4;
//X+Y symmetric - rotate 90 degrees
case 4:
if(%bAngle % 2 == 0)
%bAngle = (%bAngle + 1) % 4;
else
%bAngle = (%bAngle + 3) % 4;
//X-Y symmetric - rotate -90 degrees
case 5:
if(%bAngle % 2 == 0)
%bAngle = (%bAngle + 3) % 4;
else
%bAngle = (%bAngle + 1) % 4;
}
}
else if(%axis == 1)
{
//Handle symmetries
switch($ND::Symmetry[%datablock])
{
//Asymmetric
case 0:
if(%db = $ND::SymmetryXDatablock[%datablock])
{
%datablock = %db;
%bAngle = (%bAngle + $ND::SymmetryXOffset[%datablock]) % 4;
//Pair is made on X, so apply mirror logic for X afterwards
if(%bAngle % 2 == 0)
%bAngle = (%bAngle + 2) % 4;
}
else
{
messageClient(%client, '', "\c6Sorry, your ghost brick is asymmetric and cannot be mirrored.");
return;
}
//Do nothing for fully symmetric
//X symmetric - rotate 180 degrees if brick is angled 90 or 270 degrees
case 2:
if(%bAngle % 2 == 0)
%bAngle = (%bAngle + 2) % 4;
//Y symmetric - rotate 180 degrees if brick is angled 0 or 180 degrees
case 3:
if(%bAngle % 2 == 1)
%bAngle = (%bAngle + 2) % 4;
//X+Y symmetric - rotate 90 degrees
case 4:
if(%bAngle % 2 == 1)
%bAngle = (%bAngle + 1) % 4;
else
%bAngle = (%bAngle + 3) % 4;
//X-Y symmetric - rotate -90 degrees
case 5:
if(%bAngle % 2 == 1)
%bAngle = (%bAngle + 3) % 4;
else
%bAngle = (%bAngle + 1) % 4;
}
}
else
{
//Change datablock if asymmetric
if(!$ND::SymmetryZ[%datablock])
{
if(%db = $ND::SymmetryZDatablock[%datablock])
{
%datablock = %db;
%bAngle = (%bAngle + $ND::SymmetryZOffset[%datablock]) % 4;
}
else
{
messageClient(%client, '', "\c6Sorry, your ghost brick is not vertically symmetric and cannot be mirrored.");
return;
}
}
}
//Apply datablock
if(%brick.getDatablock() != %datablock)
%brick.setDatablock(%datablock);
switch(%bAngle)
{
case 0: %bRot = "1 0 0 0";
case 1: %bRot = "0 0 1 1.5708";
case 2: %bRot = "0 0 1 3.14150";
case 3: %bRot = "0 0 -1 1.5708";
}
//Apply transform
%brick.setTransform(%bPos SPC %bRot);
}
//Supercut helpers
///////////////////////////////////////////////////////////////////////////
//Creates simple brick lookup table
function ndCreateSimpleBrickTable()
{
deleteVariables("$ND::SimpleBrick*");
%max = getDatablockGroupSize();
%file = new FileObject();
%sorter = new GuiTextListCtrl();
for(%i = 0; %i < %max; %i++)
{
%db = getDatablock(%i);
if(%db.getClassName() $= "FxDtsBrickData")
{
//Skip unsuitable bricks
if((
%db.isWaterBrick
|| %db.hasPrint
|| %db.isSlyrBrick
|| %db.uiName $= ""
|| %db.ndDontUseForFill
|| %db.category $= "Special"
|| %db.isLogicGate
) && (
ndSubsetOfDatablock(%db)==$ND::SubsetDefault
)
){
continue;
}
%db.ndSubset = ndSubsetOfDatablock(%db);
%file.openForRead(%db.brickFile);
%file.readLine();
//We only want simple bricks here
if(%file.readLine() $= "BRICK")
{
//Skip brick sizes that we already have
if(!$ND::SimpleBrickBlock[%db.brickSizeX, %db.brickSizeY, %db.BrickSizeZ, %db.ndSubset]){
%sorter.addRow(%db, %db.getVolume());
}
$ND::SimpleBrickBlock[%db.brickSizeX, %db.brickSizeY, %db.BrickSizeZ, %db.ndSubset] = true;
}
%file.close();
}
}
%file.delete();
//Sort the bricks by volume
%sorter.sortNumerical(0, 1);
//Copy sorted bricks to global variable array
$ND::SimpleBrickCount = %sorter.rowCount();
for(%i = 0; %i < $ND::SimpleBrickCount; %i++)
{
%db = %sorter.getRowId(%i);
%volume = %sorter.getRowText(%i);
$ND::SimpleBrick[%i] = %db;
$ND::SimpleBrickVolume[%i] = %volume;
$ND::SimpleBrickSubset[%i] = %db.ndSubset;
//Ensure X < Y in lookup table
if(%db.brickSizeX <= %db.brickSizeY)
{
$ND::SimpleBrickSizeX[%i] = %db.brickSizeX;
$ND::SimpleBrickSizeY[%i] = %db.brickSizeY;
$ND::SimpleBrickRotated[%i] = false;
}
else
{
$ND::SimpleBrickSizeX[%i] = %db.brickSizeY;
$ND::SimpleBrickSizeY[%i] = %db.brickSizeX;
$ND::SimpleBrickRotated[%i] = true;
}
$ND::SimpleBrickSizeZ[%i] = %db.brickSizeZ;
}
%sorter.delete();
$ND::SimpleBrickTableCreated = true;
}
//Find the largest (volume) brick that fits inside the area
function ndGetLargestBrickId(%x, %y, %z, %subset)
{
if(!$ND::SimpleBrickTableCreated)
ndCreateSimpleBrickTable();
%maxVolume = %x * %y * %z;
%start = $ND::SimpleBrickCount - 1;
if($ND::SimpleBrickVolume[%start] > %maxVolume)
{
//Use binary search to find the largest volume that
//is smaller or equal to the volume of the area
%bound1 = 0;
%bound2 = %start;
while(%bound1 < %bound2)
{
%i = mCeil((%bound1 + %bound2) / 2);
%volume = $ND::SimpleBrickVolume[%i];
if(%volume > %maxVolume)
{
%bound2 = %i - 1;
continue;
}
if(%volume <= %maxVolume)
{
%bound1 = %i + 1;
continue;
}
}
%start = %bound2;
}
%bestIndex = -1;
//Go down the list until a brick fits on all 3 axis
for(%i = %start; %i >= 0; %i--)
{
if(
$ND::SimpleBrickSizeX[%i] <= %x
&& $ND::SimpleBrickSizeY[%i] <= %y
&& $ND::SimpleBrickSizeZ[%i] <= %z
&& $ND::SimpleBrickSubset[%i] == %subset
) {
return %i;
}
}
return -1;
}
//Fill an area with bricks
function ndFillAreaWithBricks(%pos1, %pos2)
{
%pos1_x = getWord(%pos1, 0);
%pos1_y = getWord(%pos1, 1);
%pos1_z = getWord(%pos1, 2);
%pos2_x = getWord(%pos2, 0);
%pos2_y = getWord(%pos2, 1);
%pos2_z = getWord(%pos2, 2);
%size_x = %pos2_x - %pos1_x;
%size_y = %pos2_y - %pos1_y;
%size_z = %pos2_z - %pos1_z;
if(%size_x < 0.05 || %size_y < 0.05 || %size_z < 0.05)
return;
if(%size_x > %size_y)
{
%tmp = %size_y;
%size_y = %size_x;
%size_x = %tmp;
%rotated = true;
}
%brickId = ndGetLargestBrickId(%size_x * 2 + 0.05, %size_y * 2 + 0.05, %size_z * 5 + 0.02, $ND::FillBrickSubset);
if(!%rotated)
{
%pos3_x = %pos1_x + $ND::SimpleBrickSizeX[%brickId] / 2;
%pos3_y = %pos1_y + $ND::SimpleBrickSizeY[%brickId] / 2;
}
else
{
%pos3_x = %pos1_x + $ND::SimpleBrickSizeY[%brickId] / 2;
%pos3_y = %pos1_y + $ND::SimpleBrickSizeX[%brickId] / 2;
}
%pos3_z = %pos1_z + $ND::SimpleBrickSizeZ[%brickId] / 5;
%plantPos = (%pos1_x + %pos3_x) / 2 SPC (%pos1_y + %pos3_y) / 2 SPC (%pos1_z + %pos3_z) / 2;
if(!isObject($ND::SimpleBrick[%brickId]))
return;
%brick = new FxDTSBrick()
{
datablock = $ND::SimpleBrick[%brickId];
isPlanted = true;
client = $ND::FillBrickClient;
position = %plantPos;
rotation = (%rotated ^ $ND::SimpleBrickRotated[%brickId]) ? "0 0 1 90.0002" : "1 0 0 0";
angleID = %rotated;
colorID = $ND::FillBrickColorID;
colorFxID = $ND::FillBrickColorFxID;
shapeFxID = $ND::FillBrickShapeFxID;
printID = 0;
};
//This will call ::onLoadPlant instead of ::onPlant
%prev1 = $Server_LoadFileObj;
%prev2 = $LastLoadedBrick;
$Server_LoadFileObj = %brick;
$LastLoadedBrick = %brick;
//Add to brickgroup
$ND::FillBrickGroup.add(%brick);
//Attempt plant
%error = %brick.plant();
//Restore variable
$Server_LoadFileObj = %prev1;
$LastLoadedBrick = %prev2;
if(!%error || %error == 2)
{
//Set trusted
if(%brick.getNumDownBricks())
%brick.stackBL_ID = %brick.getDownBrick(0).stackBL_ID;
else if(%brick.getNumUpBricks())
%brick.stackBL_ID = %brick.getUpBrick(0).stackBL_ID;
else
%brick.stackBL_ID = $ND::FillBrickBL_ID;
%brick.trustCheckFinished();
%brick.setRendering($ND::FillBrickRendering);
%brick.setColliding($ND::FillBrickColliding);
%brick.setRayCasting($ND::FillBrickRayCasting);
//Instantly ghost the brick to all spawned clients (wow hacks)
for(%j = 0; %j < $ND::NumSpawnedClients; %j++)
{
%cl = $ND::SpawnedClient[%j];
%brick.scopeToClient(%cl);
%brick.clearScopeToClient(%cl);
}
$ND::FillBrickCount++;
}
else
%brick.delete();
if((%pos3_x + 0.05) < %pos2_x)
ndFillAreaWithBricks(%pos3_x SPC %pos1_y SPC %pos1_z, %pos2_x SPC %pos2_y SPC %pos2_z);
if((%pos3_y + 0.05) < %pos2_y)
ndFillAreaWithBricks(%pos1_x SPC %pos3_y SPC %pos1_z, %pos3_x SPC %pos2_y SPC %pos2_z);
if((%pos3_z + 0.02) < %pos2_z)
ndFillAreaWithBricks(%pos1_x SPC %pos1_y SPC %pos3_z, %pos3_x SPC %pos3_y SPC %pos2_z);
}
//Client finished supercut, now fill bricks
function GameConnection::doFillBricks(%this, %subset)
{
//Set variables for the fill brick function
$ND::FillBrickGroup = %this.brickGroup;
$ND::FillBrickClient = %this;
$ND::FillBrickBL_ID = %this.bl_id;
$ND::FillBrickColorID = %this.currentColor;
$ND::FillBrickColorFxID = 0;
$ND::FillBrickShapeFxID = 0;
$ND::FillBrickRendering = true;
$ND::FillBrickColliding = true;
$ND::FillBrickRayCasting = true;
$ND::FillBrickSubset = %subset;
%box = %this.ndSelectionBox.getWorldBox();
$ND::FillBrickCount = 0;
ndUpdateSpawnedClientList();
ndFillAreaWithBricks(getWords(%box, 0, 2), getWords(%box, 3, 5));
//%s = ($ND::FillBrickCount == 1 ? "" : "s");
//messageClient(%this, '', "\c6Filled in \c3" @ $ND::FillBrickCount @ "\c6 brick" @ %s);
}
$ND::SubsetDefault = 0;
$ND::SubsetLogicWire = 1;
$ND::SubsetLogicBuffer = 2;
$ND::SubsetLogicBufferAl = 3;
$ND::SubsetLogicDff = 4;
$ND::SubsetLogicDffAl = 5;
$ND::SubsetLogicEnabler = 6;
$ND::SubsetLogicEnablerAl = 7;
// Which subset of fill bricks to use - normal or wire
function ndSubsetOfDatablock(%data){
if(%data.isLogic) {
if(%data.isLogicWire) {
return $ND::SubsetLogicWire;
} else if(strStr(%data.uiName, "Buffer") == 0) {
return (strStr(%data.uiName, "Active Low")==-1) ? $ND::SubsetLogicBuffer : $ND::SubsetLogicBufferAl;
} else if(strStr(%data.uiName, "D FlipFlop") == 0) {
return (strStr(%data.uiName, "Active Low")==-1) ? $ND::SubsetLogicDff : $ND::SubsetLogicDffAl;
}else if(strStr(%data.uiName, "Enabler") == 0) {
return (strStr(%data.uiName, "Active Low")==-1) ? $ND::SubsetLogicEnabler : $ND::SubsetLogicEnablerAl;
} else {
return $ND::SubsetDefault;
}
} else {
return $ND::SubsetDefault;
}
}
function ndLookupSubsetName(%name) {
%subset = $ND::Subset[%name];
return (%subset !$= "") ? %subset : $ND::SubsetDefault;
}

View File

@ -0,0 +1,71 @@
// Sends a handshake to new clients to obtain their duplicator version.
// -------------------------------------------------------------------
package NewDuplicator_Server
{
//Send handshake request to new client
function GameConnection::autoAdminCheck(%this)
{
%this.ndClient = false;
%this.ndVersion = "0.0.0";
commandToClient(%this, 'ndHandshake', $ND::Version);
return parent::autoAdminCheck(%this);
}
};
//Client responded, so he has new duplicator
function serverCmdNdHandshake(%this, %version)
{
cancel(%this.ndHandshakeTimeout);
%this.ndClient = true;
%this.ndVersion = %version;
//Inform client whether he has an outdated version
switch(ndCompareVersion($ND::Version, %version))
{
case 1:
%m = "\c6Your version of the \c3New Duplicator\c6 is outdated! Some features might not work. ";
%m = %m @ "(Server Version: \c3" @ $ND::Version @ "\c6 | Your Version: \c0" @ %version @ "\c6)";
//messageClient(%this, '', %m);
case 2:
//Hide this message on long-running dedicated servers
if($Sim::Time < 86400)
{
%m = "\c6Your version of the \c3New Duplicator\c6 is newer than the server's! Ask the host to update it! ";
%m = %m @ "(Server Version: \c0" @ $ND::Version @ "\c6 | Your Version: \c3" @ %version @ "\c6)";
//messageClient(%this, '', %m);
}
}
}
//Compares two version numbers (major.minor.patch)
function ndCompareVersion(%ver1, %ver2)
{
%ver1 = strReplace(%ver1, ".", " ");
%ver2 = strReplace(%ver2, ".", " ");
%count = getMax(getWordCount(%ver1), getWordCount(%ver2));
for(%i = 0; %i < %count; %i ++)
{
%v1 = getWord(%ver1, %i);
%v2 = getWord(%ver2, %i);
if(%v1 > %v2)
return 1;
else if(%v1 < %v2)
return 2;
}
return 0;
}
//Send handshakes to all clients
function ndResendHandshakes()
{
for(%i = 0; %i < ClientGroup.getCount(); %i++)
commandToClient(ClientGroup.getObject(%i), 'ndHandshake', $ND::Version);
}

106
scripts/server/highlight.cs Normal file
View File

@ -0,0 +1,106 @@
// Handles highlighting and de-highlighting large groups of bricks.
// -------------------------------------------------------------------
//Highlight group data $NDH::*
// $NDH::LastId : The id of the last created highlight group
// $NDH::Count : Total number of active highlight groups
//
// $NDHN[brick] : Number of groups a brick is in
// $NDHF[brick] : Original color fx of the brick
//
// $NDH[group] : Count of bricks in a group
// $NDH[group, i] : Brick in group at position i
//Reserve a highlight group id
function ndNewHighlightGroup()
{
//Increase group number
$NDH::LastId++;
$NDH::Count++;
//Set initial count
$NDH[$NDH::LastId] = 0;
//Assign free id
return $NDH::LastId;
}
//Remove highlight group and clean up garbage variables
function ndRemoveHighlightGroup(%group)
{
//Don't delete groups that don't exist
if($NDH[%group] $= "")
return;
//Lower group number
$NDH::Count--;
//Clear count to allow reuse of index
$NDH[%group] = "";
//Cancel schedules
cancel($NDHS[%group]);
//If this is the most recently created group, pretend it never existed
if($NDH::LastId == %group)
$NDH::LastId--;
//If this is the last highlight group, just delete ALL highlight variables
if($NDH::Count < 1)
deleteVariables("$NDH*");
}
//Add a brick to a highlight group
function ndHighlightBrick(%group, %brick)
{
//If brick is not highlighted, do that
if(!$NDHN[%brick])
{
$NDHF[%brick] = %brick.colorFxID;
%brick.setColorFx(3);
}
//Increase group number of this brick
$NDHN[%brick]++;
//Add brick to highlight group
$NDH[%group, ($NDH[%group]++) - 1] = %brick;
}
//Start de-highlighting bricks
function ndStartDeHighlight(%group)
{
//Don't do this if already de-highlighting
%t = getTimeRemaining($NDHS[%group]);
if(%t > 66 || %t == 0)
{
cancel($NDHS[%group]);
ndTickDeHighlight(%group, 0);
}
}
//Tick de-highlighting bricks
function ndTickDeHighlight(%group, %start)
{
%end = $NDH[%group];
if(%end - %start > $Pref::Server::ND::ProcessPerTick)
%end = %start + $Pref::Server::ND::ProcessPerTick;
else
%lastTick = true;
for(%i = %start; %i < %end; %i++)
{
%brick = $NDH[%group, %i];
//If the brick is in no more groups, de-highlight it
if(isObject(%brick) && !($NDHN[%brick]--))
%brick.setColorFx($NDHF[%brick]);
}
if(!%lastTick)
$NDHS[%group] = schedule(30, 0, ndTickDeHighlight, %group, %end);
else
ndRemoveHighlightGroup(%group);
}

297
scripts/server/images.cs Normal file
View File

@ -0,0 +1,297 @@
// Handles interactions with the handheld duplicator item.
// -------------------------------------------------------------------
//Set which image a client should use
function GameConnection::ndSetImage(%this, %image)
{
%image = %image.getId();
if(%image != %this.ndImage)
{
%this.ndImage = %image;
if(%this.ndEquipped)
{
%this.ndIgnoreNextMount = true;
%this.player.schedule(0, updateArm, %image);
%this.player.schedule(0, mountImage, %image, 0);
}
}
}
//Mount the correct image when the item is equipped
function ND_Item::onUse(%this, %player, %slot)
{
%image = %player.client.ndImage;
if(!isObject(%image))
%image = ND_Image;
%player.updateArm(%image);
%player.mountImage(%image, 0);
%player.client.ndEquippedFromItem = true;
}
package NewDuplicator_Server
{
//Start select mode when duplicator is equipped
function ND_Image::onMount(%this, %player, %slot)
{
parent::onMount(%this, %player, %slot);
%player.ndEquipped();
}
function ND_Image_Blue::onMount(%this, %player, %slot)
{
parent::onMount(%this, %player, %slot);
%player.ndEquipped();
}
function ND_Image_Box::onMount(%this, %player, %slot)
{
parent::onMount(%this, %player, %slot);
%player.ndEquipped();
}
//Cancel mode when duplicator is unequipped
function ND_Image::onUnMount(%this, %player, %slot)
{
parent::onUnMount(%this, %player, %slot);
%player.ndUnEquipped();
}
function ND_Image_Blue::onUnMount(%this, %player, %slot)
{
parent::onUnMount(%this, %player, %slot);
%player.ndUnEquipped();
}
function ND_Image_Box::onUnMount(%this, %player, %slot)
{
parent::onUnMount(%this, %player, %slot);
%player.ndUnEquipped();
}
};
//Start the swinging animation
function ND_Image::onPreFire(%this, %player, %slot)
{
%player.playThread(2, shiftTo);
}
function ND_Image_Blue::onPreFire(%this, %player, %slot)
{
%player.playThread(2, shiftTo);
}
function ND_Image_Box::onPreFire(%this, %player, %slot)
{
%player.playThread(2, shiftTo);
}
//Handle selecting things
function ND_Image::onFire(%this, %player, %slot)
{
%player.ndFired();
}
function ND_Image_Blue::onFire(%this, %player, %slot)
{
%player.ndFired();
}
function ND_Image_Box::onFire(%this, %player, %slot)
{
%player.ndFired();
}
//Duplicator was equipped
function Player::ndEquipped(%this)
{
%client = %this.client;
if(%this.isHoleBot || %this.isSlayerBot || !isObject(%client))
return;
if(%client.ndIgnoreNextMount)
{
%client.ndIgnoreNextMount = false;
return;
}
if($Pref::Server::ND::AdminOnly && !%client.isAdmin)
{
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Oops! The duplicator is admin only.", 5);
return;
}
%client.ndEquipped = true;
//Remove temp brick so it doesn't overlap the selection box
if(isObject(%this.tempBrick))
%this.tempBrick.delete();
//Should resume last used select mode
if(!%client.ndModeIndex)
%client.ndSetMode(%client.ndLastSelectMode);
}
//Duplicator was unequipped
function Player::ndUnEquipped(%this)
{
%client = %this.client;
if(%this.isHoleBot || %this.isSlayerBot || !isObject(%client))
return;
if(%client.ndIgnoreNextMount)
return;
if(%client.ndModeIndex && !%client.ndMode.allowUnMount)
%client.ndKillMode();
%client.ndEquipped = false;
}
//Duplicator was fired
function Player::ndFired(%this)
{
%client = %this.client;
if(!isObject(%client) || !%client.ndModeIndex || !%client.ndMode.allowSelecting)
return;
if(isObject(%client.minigame) && !%client.minigame.enablebuilding)
{
commandToClient(%client, 'centerPrint', "<font:Verdana:20>\c6Oops! Building is disabled.", 5);
return;
}
//Support for Script_RaycastOffTools by Conan
if(%this.isRaycastTool)
%mask = $TypeMasks::FxBrickObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType;
else
%mask = $TypeMasks::FxBrickAlwaysObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType;
%start = %this.getEyePoint();
%dir = %this.getEyeVector();
//Octree::Raycast fails to detect close bricks (~1TU) with a long raycast, so we'll do 3.
//First a very short one of 1 TU to detect very close bricks, then a slightly longer one
//of 10 TU, and finally the long range one of 1000.
%len[0] = 1;
%len[1] = 10;
%len[2] = 1000;
for(%i = 0; %i < 3; %i++)
{
%end = vectorAdd(%start, vectorScale(%dir, %len[%i]));
%ray = containerRaycast(%start, %end, %mask, %this);
if(isObject(%obj = firstWord(%ray)))
break;
}
if(!isObject(%obj))
return;
%position = posFromRaycast(%ray);
%normal = normalFromRaycast(%ray);
//Can't directly spawn an explosion, must use a projectile
%data = %client.ndImage.projectile;
if(!isObject(%data))
%data = ND_HitProjectile;
%proj = new Projectile()
{
datablock = %data;
initialPosition = %position;
initialVelocity = %normal;
};
//Pass on the selected object to the dupli mode
%client.ndMode.onSelectObject(%client, %obj, %position, %normal);
}
package NewDuplicator_Server
{
//Automatically start the "ambient" animation on duplicator items
function ND_Item::onAdd(%this, %obj)
{
parent::onAdd(%this, %obj);
%obj.playThread(0, ambient);
//Fix colorshift bullshit
%obj.schedule(100, setNodeColor, "ALL", %this.colorShiftColor);
}
//Prevent accidently unequipping the duplicator
function serverCmdUnUseTool(%client)
{
if(%client.ndLastEquipTime + 1.5 > $Sim::Time)
return;
if(%client.ndModeIndex == $NDM::StackSelect || %client.ndModeIndex == $NDM::BoxSelect)
{
%client.ndToolSchedule = %client.schedule(100, ndUnUseTool);
return;
}
parent::serverCmdUnUseTool(%client);
}
//Prevent creating ghost bricks in modes that allow un-mount
function BrickDeployProjectile::onCollision(%this, %obj, %col, %fade, %pos, %normal)
{
%client = %obj.client;
if(isObject(%client) && %client.ndModeIndex)
%client.ndMode.onSelectObject(%client, %col, %pos, %normal);
else
parent::onCollision(%this, %obj, %col, %fade, %pos, %normal);
}
//Handle ghost brick movements from New Brick Tool
function placeNewGhostBrick(%client, %pos, %normal, %noOrient)
{
if(!isObject(%client) || !%client.ndModeIndex)
return parent::placeNewGhostBrick(%client, %pos, %normal, %noOrient);
if(%client.ndModeIndex == $NDM::PlantCopy)
{
if(!%noOrient)
{
%angleID = getAngleIDFromPlayer(%client.getControlObject()) - %client.ndSelection.angleIDReference;
%rotation = ((4 - %angleID) - %client.ndSelection.ghostAngleID) % 4;
if(%rotation != 0)
%client.ndSelection.rotateGhostBricks(%rotation, %client.ndPivot);
}
return NDM_PlantCopy.moveBricksTo(%client, %pos, %normal);
}
%client.ndMode.onSelectObject(%client, 0, %pos, %normal);
}
};
//Fix for equipping paint can calling unUseTool
function GameConnection::ndUnUseTool(%this)
{
%player = %this.player;
if(%this.isTalking)
serverCmdStopTalking(%this);
if(!isObject(%player))
return;
%player.currTool = -1;
%this.currInv = -1;
%this.currInvSlot = -1;
%player.unmountImage(0);
%player.playThread(1, "root");
}

497
scripts/server/modes.cs Normal file
View File

@ -0,0 +1,497 @@
// Handles the duplicator state machine. Does not validate transitions!
// -------------------------------------------------------------------
//Base class for all duplicator modes
///////////////////////////////////////////////////////////////////////////
function NewDuplicatorMode::onStartMode(%this, %client, %lastMode){}
function NewDuplicatorMode::onChangeMode(%this, %client, %nextMode){}
function NewDuplicatorMode::onKillMode(%this, %client){}
function NewDuplicatorMode::onSelectObject(%this, %client, %obj, %pos, %normal){}
function NewDuplicatorMode::onLight(%this, %client){}
function NewDuplicatorMode::onNextSeat(%this, %client){}
function NewDuplicatorMode::onPrevSeat(%this, %client){}
function NewDuplicatorMode::onShiftBrick(%this, %client, %x, %y, %z){}
function NewDuplicatorMode::onSuperShiftBrick(%this, %client, %x, %y, %z){}
function NewDuplicatorMode::onRotateBrick(%this, %client, %direction){}
function NewDuplicatorMode::onPlantBrick(%this, %client){}
function NewDuplicatorMode::onCancelBrick(%this, %client){}
function NewDuplicatorMode::onCopy(%this, %client)
{
messageClient(%client, '', "\c6Copy can not be used in your current duplicator mode.");
}
function NewDuplicatorMode::onPaste(%this, %client)
{
messageClient(%client, '', "\c6Paste can not be used in your current duplicator mode.");
}
function NewDuplicatorMode::onCut(%this, %client)
{
messageClient(%client, '', "\c6Cut can not be used in your current duplicator mode.");
}
function NewDuplicatorMode::getBottomPrint(%this, %client){}
//Registering duplicator modes
///////////////////////////////////////////////////////////////////////////
//Possible mode indices
$NDM::Disabled = 0;
$NDM::BoxSelect = 1;
$NDM::BoxSelectProgress = 2;
$NDM::CutProgress = 3;
$NDM::FillColor = 4;
$NDM::FillColorProgress = 5;
$NDM::StackSelect = 6;
$NDM::StackSelectProgress = 7;
$NDM::PlantCopy = 8;
$NDM::PlantCopyProgress = 9;
$NDM::WrenchProgress = 10;
$NDM::SaveProgress = 11;
$NDM::LoadProgress = 12;
$NDM::SuperCutProgress = 13;
//Create all the pseudo-classes to handle callbacks
function ndRegisterDuplicatorModes()
{
echo("ND: Registering duplicator modes");
//Disabled duplicator mode (does nothing)
ND_ServerGroup.add(
new ScriptObject(NDM_Disabled)
{
class = "NewDuplicatorMode";
index = $NDM::Disabled;
image = "ND_Image";
spin = false;
allowSelecting = false;
allowUnMount = false;
}
);
//Box Select duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_BoxSelect)
{
class = "NewDuplicatorMode";
index = $NDM::BoxSelect;
image = "ND_Image_Box";
spin = false;
allowSelecting = true;
allowUnMount = false;
}
);
//Box Select Progress duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_BoxSelectProgress)
{
class = "NewDuplicatorMode";
index = $NDM::BoxSelectProgress;
image = "ND_Image_Box";
spin = true;
allowSelecting = false;
allowUnMount = false;
}
);
//Cut Progress duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_CutProgress)
{
class = "NewDuplicatorMode";
index = $NDM::CutProgress;
image = "any";
spin = true;
allowSelecting = false;
allowUnMount = false;
}
);
//Fill Color duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_FillColor)
{
class = "NewDuplicatorMode";
index = $NDM::FillColor;
image = "any";
spin = false;
allowSelecting = false;
allowUnMount = false;
}
);
//Fill Color Progress duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_FillColorProgress)
{
class = "NewDuplicatorMode";
index = $NDM::FillColorProgress;
image = "any";
spin = true;
allowSelecting = false;
allowUnMount = false;
}
);
//Plant Copy duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_PlantCopy)
{
class = "NewDuplicatorMode";
index = $NDM::PlantCopy;
image = "ND_Image_Blue";
spin = false;
allowSelecting = true;
allowUnMount = true;
}
);
//Plant Copy Progress duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_PlantCopyProgress)
{
class = "NewDuplicatorMode";
index = $NDM::PlantCopyProgress;
image = "ND_Image_Blue";
spin = true;
allowSelecting = false;
allowUnMount = true;
}
);
//Stack Select duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_StackSelect)
{
class = "NewDuplicatorMode";
index = $NDM::StackSelect;
image = "ND_Image";
spin = false;
allowSelecting = true;
allowUnMount = false;
}
);
//Stack Select Progress duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_StackSelectProgress)
{
class = "NewDuplicatorMode";
index = $NDM::StackSelectProgress;
image = "ND_Image";
spin = true;
allowSelecting = false;
allowUnMount = false;
}
);
//Wrench Progress duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_WrenchProgress)
{
class = "NewDuplicatorMode";
index = $NDM::WrenchProgress;
image = "any";
spin = true;
allowSelecting = false;
allowUnMount = false;
}
);
//Save Progress duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_SaveProgress)
{
class = "NewDuplicatorMode";
index = $NDM::SaveProgress;
image = "any";
spin = true;
allowSelecting = false;
allowUnMount = false;
}
);
//Load Progress duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_LoadProgress)
{
class = "NewDuplicatorMode";
index = $NDM::LoadProgress;
image = "any";
spin = true;
allowSelecting = false;
allowUnMount = false;
}
);
//Supercut Progress duplicator mode
ND_ServerGroup.add(
new ScriptObject(NDM_SuperCutProgress)
{
class = "NewDuplicatorMode";
index = $NDM::SuperCutProgress;
image = "ND_Image_Box";
spin = true;
allowSelecting = false;
allowUnMount = false;
}
);
//If clients already exist, reset their modes
for(%i = 0; %i < ClientGroup.getCount(); %i++)
{
%cl = ClientGroup.getObject(%i);
%cl.ndPivot = true;
%cl.ndLimited = true;
%cl.ndDirection = true;
%cl.ndForcePlant = false;
%cl.ndImage = ND_Image.getId();
%cl.ndMode = NDM_Disabled;
%cl.ndModeIndex = $NDM::Disabled;
%cl.ndLastSelectMode = NDM_StackSelect;
}
}
//Switching modes
///////////////////////////////////////////////////////////////////////////
//Change duplication mode
function GameConnection::ndSetMode(%this, %newMode)
{
%oldMode = %this.ndMode;
if(%oldMode.index == %newMode.index)
return;
%this.ndMode = %newMode;
%this.ndModeIndex = %newMode.index;
%oldMode.onChangeMode(%this, %newMode.index);
%newMode.onStartMode(%this, %oldMode.index);
//Enable keybinds
if(!%oldMode.index)
{
commandToClient(%this, 'ndEnableKeybinds', true);
%client.ndMultiselect = false;
}
//Change image
if(%newMode.image !$= "any")
%this.ndSetImage(nameToId(%newMode.image));
//Start or stop spinning
%this.player.setImageLoaded(0, !%newMode.spin);
}
//Kill duplication mode
function GameConnection::ndKillMode(%this)
{
if(!%this.ndModeIndex)
return;
%this.ndMode.onKillMode(%this);
%this.ndMode = NDM_Disabled;
%this.ndModeIndex = $NDM::Disabled;
%this.ndUpdateBottomPrint();
//Disable keybinds
commandToClient(%this, 'ndEnableKeybinds', false);
}
//Bottomprints
///////////////////////////////////////////////////////////////////////////
//Update the bottomprint
function GameConnection::ndUpdateBottomPrint(%this)
{
if(%this.ndModeIndex)
commandToClient(%this, 'bottomPrint', %this.ndMode.getBottomPrint(%this), 0, true);
else
commandToClient(%this, 'clearBottomPrint');
}
//Format bottomprint message with left and right justified text
function ndFormatMessage(%title, %l0, %r0, %l1, %r1, %l2, %r2)
{
%message = "<font:Arial:22>";
//Last used alignment, false = left | true = right
%align = false;
if(strStr("\c0\c1\c2\c3\c4\c5\c6\c7\c8\c9", getSubStr(%title, 0, 1)) < 0)
%message = %message @ "\c6";
%message = %message @ %title @ "\n<font:Verdana:16>";
for(%i = 0; strLen(%l[%i]) || strLen(%r[%i]); %i++)
{
if(strLen(%l[%i]))
{
if(%align)
%message = %message @ "<just:left>";
if(strStr("\c0\c1\c2\c3\c4\c5\c6\c7\c8\c9", getSubStr(%l[%i], 0, 1)) < 0)
%message = %message @ "\c6";
%message = %message @ %l[%i];
%align = false;
}
if(strLen(%r[%i]))
{
if(!%align)
%message = %message @ "<just:right>";
if(strStr("\c0\c1\c2\c3\c4\c5\c6\c7\c8\c9", getSubStr(%r[%i], 0, 1)) < 0)
%message = %message @ "\c6";
%message = %message @ %r[%i] @ " ";
%align = true;
}
%message = %message @ "\n";
}
return %message @ " ";
}
//Connecting, disconnecting, death
///////////////////////////////////////////////////////////////////////////
package NewDuplicator_Server
{
//Set initial variables on join
function GameConnection::onClientEnterGame(%this)
{
%this.ndPivot = true;
%this.ndLimited = true;
%this.ndDirection = true;
%this.ndForcePlant = false;
%this.ndImage = ND_Image.getId();
%this.ndMode = NDM_Disabled;
%this.ndModeIndex = $NDM::Disabled;
%this.ndLastSelectMode = NDM_StackSelect;
parent::onClientEnterGame(%this);
}
//Kill duplicator mode when a client leaves
function GameConnection::onClientLeaveGame(%this)
{
if(%this.ndModeIndex)
%this.ndKillMode(%this);
%this.ndEquipped = false;
//Remove from client lists of selections
for(%i = 0; %i < ND_ServerGroup.getCount(); %i++)
{
%obj = ND_ServerGroup.getObject(%i);
if(%obj.getName() $= "ND_Selection")
{
for(%j = 0; %j < %obj.numClients; %j++)
{
if($NS[%obj, "CL", %j] == %this.getId())
{
for(%k = %j; %k < (%obj.numClients - 1); %k++)
$NS[%obj, "CL", %k] = $NS[%obj, "CL", %k + 1];
%obj.numClients--;
break;
}
}
}
}
//Delete undo groups
deleteVariables("$NU" @ %this @ "_*");
%stack = %this.undoStack;
%max = %stack.head;
if(%max < %stack.tail)
%max += %stack.size;
for(%i = %stack.tail; %i < %max; %i++)
{
%val = %stack.val[%i % %stack.size];
if(getFieldCount(%val) == 2)
{
%str = getField(%val, 1);
if(
%str $= "ND_PLANT"
|| %str $= "ND_PAINT"
|| %str $= "ND_WRENCH"
){
%group = getField(%val, 0);
if(isObject(%group))
{
%group.brickCount = 0;
%group.delete();
}
}
}
}
parent::onClientLeaveGame(%this);
}
//Kill duplicator mode when a player dies
function GameConnection::onDeath(%this, %a, %b, %c, %d)
{
if(%this.ndModeIndex)
%this.ndKillMode(%this);
%this.ndEquipped = false;
parent::onDeath(%this, %a, %b, %c, %d);
}
//Kill duplicator mode when a player is force respawned
function GameConnection::spawnPlayer(%this)
{
if(%this.ndModeIndex)
%this.ndKillMode(%this);
%this.ndEquipped = false;
parent::spawnPlayer(%this);
}
};

View File

@ -0,0 +1,90 @@
// Improved versions of setNTObjectName and clearNTObjectName with much
// higher performance. Required to fix lag when clearing named bricks.
// -------------------------------------------------------------------
function SimObject::setNTObjectName(%this, %name)
{
%this = %this.getId();
%name = getSafeVariableName(trim(%name));
if(%name $= "")
{
%this.clearNTObjectName();
%this.setName("");
return;
}
//Names must start with a _ to prevent overwriting real objects
if(getSubStr(%name, 0, 1) !$= "_")
%name = "_" @ %name;
if(%this.getName() $= %name)
return;
if(isObject(%name) && !(%name.getType() & $TypeMasks::FxBrickAlwaysObjectType))
{
error("ERROR: SimObject::setNTObjectName() - Non-Brick object named \"" @ %name @ "\" already exists!");
return;
}
%this.clearNTObjectName();
%group = %this.getGroup();
%count = %group.NTObjectCount[%name] | 0;
if(!%count)
%group.addNTName(%name);
//Add a reverse lookup to remove the name much faster
%group.NTObject[%name, %count] = %this;
%group.NTObjectIndex[%name, %this] = %count;
%group.NTObjectCount[%name] = %count + 1;
%this.setName(%name);
}
function SimObject::clearNTObjectName(%this)
{
%this = %this.getId();
%group = %this.getGroup();
if(!isObject(%group))
return;
%oldName = %this.getName();
if(%oldName $= "")
return;
%index = %group.NTObjectIndex[%oldName, %this];
%count = %group.NTObjectCount[%oldName];
if(%group.NTObject[%oldName, %index] == %this)
{
//Reverse lookup works, use fast version
%lastObj = %group.NTObject[%oldName, %count - 1];
%group.NTObject[%oldName, %index] = %lastObj;
%group.NTObject[%oldName, %count - 1] = "";
%group.NTObjectIndex[%oldName, %lastObj] = %index;
%group.NTObjectIndex[%oldName, %this] = "";
%group.NTObjectCount[%oldName]--;
}
else
{
//Reverse lookup failed, use old and slow version
for(%i = 0; %i < %count; %i++)
{
if(%group.NTObject[%oldName, %i] == %this)
{
%group.NTObject[%oldName, %i] = %group.NTObject[%oldName, %count - 1];
%group.NTObject[%oldName, %count - 1] = "";
%group.NTObjectCount[%oldName]--;
break;
}
}
}
if(!%group.NTObjectCount[%oldName])
%group.removeNTName(%oldName);
}

212
scripts/server/prefs.cs Normal file
View File

@ -0,0 +1,212 @@
// Detects common services like RTB and registers perferences to them.
// -------------------------------------------------------------------
function ndRegisterPrefs()
{
//Glass prefs also set this variable so we don't need to add them seperately
if($RTB::Hooks::ServerControl)
ndRegisterPrefsToRtb();
else
ndExtendDefaultPrefValues();
ndDeleteOutdatedPrefs();
}
function ndRegisterPrefsToRtb()
{
echo("ND: Registering RTB prefs");
%trustDropDown = "list None 0 Build 1 Full 2 Self 3";
//Limits
RTB_registerPref("Admin Only", "New Duplicator | Limits", "$Pref::Server::ND::AdminOnly", "bool", "Tool_NewDuplicator", false, false, false, "");
RTB_registerPref("Fill Paint Admin Only", "New Duplicator | Limits", "$Pref::Server::ND::PaintAdminOnly", "bool", "Tool_NewDuplicator", false, false, false, "");
RTB_registerPref("Fill Paint Fx Admin Only", "New Duplicator | Limits", "$Pref::Server::ND::PaintFxAdminOnly", "bool", "Tool_NewDuplicator", true, false, false, "");
RTB_registerPref("Fill Wrench Admin Only", "New Duplicator | Limits", "$Pref::Server::ND::WrenchAdminOnly", "bool", "Tool_NewDuplicator", true, false, false, "");
RTB_registerPref("Floating Bricks Admin Only", "New Duplicator | Limits", "$Pref::Server::ND::FloatAdminOnly", "bool", "Tool_NewDuplicator", true, false, false, "");
RTB_registerPref("Save Admin Only", "New Duplicator | Limits", "$Pref::Server::ND::SaveAdminOnly", "bool", "Tool_NewDuplicator", true, false, false, "");
RTB_registerPref("Load Admin Only", "New Duplicator | Limits", "$Pref::Server::ND::LoadAdminOnly", "bool", "Tool_NewDuplicator", false, false, false, "");
RTB_registerPref("Fill Bricks Admin Only", "New Duplicator | Limits", "$Pref::Server::ND::FillBricksAdminOnly", "bool", "Tool_NewDuplicator", true, false, false, "");
//Settings
RTB_RegisterPref("Trust Limit", "New Duplicator | Settings", "$Pref::Server::ND::TrustLimit", %trustDropDown, "Tool_NewDuplicator", 2, false, false, "");
RTB_RegisterPref("Admin Trust Bypass (Select)", "New Duplicator | Settings", "$Pref::Server::ND::AdminTrustBypass1", "bool", "Tool_NewDuplicator", true, false, false, "");
RTB_RegisterPref("Admin Trust Bypass (Edit)", "New Duplicator | Settings", "$Pref::Server::ND::AdminTrustBypass2", "bool", "Tool_NewDuplicator", false, false, false, "");
RTB_RegisterPref("Select Public Bricks", "New Duplicator | Settings", "$Pref::Server::ND::SelectPublicBricks", "bool", "Tool_NewDuplicator", true, false, false, "");
RTB_registerPref("Max Bricks (Admin)", "New Duplicator | Settings", "$Pref::Server::ND::MaxBricksAdmin", "int 1000 1000000", "Tool_NewDuplicator", 1000000, false, false, "");
RTB_registerPref("Max Bricks (Player)", "New Duplicator | Settings", "$Pref::Server::ND::MaxBricksPlayer", "int 1000 1000000", "Tool_NewDuplicator", 50000, false, false, "");
RTB_registerPref("Max Box Size (Admin)", "New Duplicator | Settings", "$Pref::Server::ND::MaxBoxSizeAdmin", "int 1 50000", "Tool_NewDuplicator", 1024, false, false, "");
RTB_registerPref("Max Box Size (Player)", "New Duplicator | Settings", "$Pref::Server::ND::MaxBoxSizePlayer", "int 1 50000", "Tool_NewDuplicator", 64, false, false, "");
RTB_registerPref("Selecting Timeout (Player)", "New Duplicator | Settings", "$Pref::Server::ND::SelectTimeoutMS", "int 0 5000", "Tool_NewDuplicator", 400, false, false, "");
RTB_registerPref("Planting Timeout (Player)", "New Duplicator | Settings", "$Pref::Server::ND::PlantTimeoutMS", "int 0 5000", "Tool_NewDuplicator", 400, false, false, "");
//Advanced
RTB_registerPref("Enable Menu Sounds", "New Duplicator | Advanced", "$Pref::Server::ND::PlayMenuSounds", "bool", "Tool_NewDuplicator", true, false, false, "");
RTB_registerPref("Max Ghost Bricks", "New Duplicator | Advanced", "$Pref::Server::ND::MaxGhostBricks", "int 1 50000", "Tool_NewDuplicator", 1500, false, false, "");
RTB_registerPref("Instant Ghost Bricks", "New Duplicator | Advanced", "$Pref::Server::ND::InstantGhostBricks", "int 1 50000", "Tool_NewDuplicator", 150, false, false, "");
RTB_registerPref("Scatter Ghost Bricks", "New Duplicator | Advanced", "$Pref::Server::ND::ScatterGhostBricks", "bool", "Tool_NewDuplicator", true, false, false, "");
RTB_registerPref("Process Bricks per Tick", "New Duplicator | Advanced", "$Pref::Server::ND::ProcessPerTick", "int 1 50000", "Tool_NewDuplicator", 300, false, false, "");
RTB_registerPref("Box Selection Chunk Size", "New Duplicator | Advanced", "$Pref::Server::ND::BoxSelectChunkDim", "int 1 50000", "Tool_NewDuplicator", 6, false, false, "");
RTB_registerPref("Create Sym Table on Start", "New Duplicator | Advanced", "$Pref::Server::ND::SymTableOnStart", "bool", "Tool_NewDuplicator", false, false, false, "");
//Restore default prefs
RTB_registerPref("Check to restore defaults", "New Duplicator | Reset Prefs", "$ND::RestoreDefaultPrefs", "bool", "Tool_NewDuplicator", false, false, false, "ndRestoreDefaultPrefs");
}
//Callback function for "Reset Prefs"
function ndRestoreDefaultPrefs()
{
if($ND::RestoreDefaultPrefs)
ndApplyDefaultPrefValues();
}
function ndExtendDefaultPrefValues()
{
echo("ND: Extending default pref values");
//Limits
if($Pref::Server::ND::AdminOnly $= "") $Pref::Server::ND::AdminOnly = false;
if($Pref::Server::ND::PaintAdminOnly $= "") $Pref::Server::ND::PaintAdminOnly = false;
if($Pref::Server::ND::PaintFxAdminOnly $= "") $Pref::Server::ND::PaintFxAdminOnly = true;
if($Pref::Server::ND::WrenchAdminOnly $= "") $Pref::Server::ND::WrenchAdminOnly = true;
if($Pref::Server::ND::FloatAdminOnly $= "") $Pref::Server::ND::FloatAdminOnly = true;
if($Pref::Server::ND::SaveAdminOnly $= "") $Pref::Server::ND::SaveAdminOnly = true;
if($Pref::Server::ND::LoadAdminOnly $= "") $Pref::Server::ND::LoadAdminOnly = false;
if($Pref::Server::ND::FillBricksAdminOnly $= "") $Pref::Server::ND::FillBricksAdminOnly = true;
//Settings
if($Pref::Server::ND::TrustLimit $= "") $Pref::Server::ND::TrustLimit = 2;
if($Pref::Server::ND::AdminTrustBypass1 $= "") $Pref::Server::ND::AdminTrustBypass1 = true;
if($Pref::Server::ND::AdminTrustBypass2 $= "") $Pref::Server::ND::AdminTrustBypass2 = false;
if($Pref::Server::ND::SelectPublicBricks $= "") $Pref::Server::ND::SelectPublicBricks = true;
if($Pref::Server::ND::MaxBricksAdmin $= "") $Pref::Server::ND::MaxBricksAdmin = 1000000;
if($Pref::Server::ND::MaxBricksPlayer $= "") $Pref::Server::ND::MaxBricksPlayer = 10000;
if($Pref::Server::ND::MaxBoxSizeAdmin $= "") $Pref::Server::ND::MaxBoxSizeAdmin = 1024;
if($Pref::Server::ND::MaxBoxSizePlayer $= "") $Pref::Server::ND::MaxBoxSizePlayer = 64;
if($Pref::Server::ND::SelectTimeoutMS $= "") $Pref::Server::ND::SelectTimeoutMS = 400;
if($Pref::Server::ND::PlantTimeoutMS $= "") $Pref::Server::ND::PlantTimeoutMS = 400;
//Advanced
if($Pref::Server::ND::PlayMenuSounds $= "") $Pref::Server::ND::PlayMenuSounds = true;
if($Pref::Server::ND::MaxGhostBricks $= "") $Pref::Server::ND::MaxGhostBricks = 1500;
if($Pref::Server::ND::InstantGhostBricks $= "") $Pref::Server::ND::InstantGhostBricks = 150;
if($Pref::Server::ND::ScatterGhostBricks $= "") $Pref::Server::ND::ScatterGhostBricks = true;
if($Pref::Server::ND::ProcessPerTick $= "") $Pref::Server::ND::ProcessPerTick = 300;
if($Pref::Server::ND::BoxSelectChunkDim $= "") $Pref::Server::ND::BoxSelectChunkDim = 6;
if($Pref::Server::ND::SymTableOnStart $= "") $Pref::Server::ND::SymTableOnStart = false;
//Always set this to false so we don't accidently reset the prefs
$ND::RestoreDefaultPrefs = false;
}
function ndApplyDefaultPrefValues()
{
echo("ND: Applying default pref values");
messageAll('', "\c6(\c3New Duplicator\c6) \c6Prefs reset to default values.");
//Limits
$Pref::Server::ND::AdminOnly = false;
$Pref::Server::ND::PaintAdminOnly = false;
$Pref::Server::ND::PaintFxAdminOnly = true;
$Pref::Server::ND::WrenchAdminOnly = true;
$Pref::Server::ND::FloatAdminOnly = true;
$Pref::Server::ND::SaveAdminOnly = true;
$Pref::Server::ND::LoadAdminOnly = false;
$Pref::Server::ND::FillBricksAdminOnly = true;
//Settings
$Pref::Server::ND::TrustLimit = 2;
$Pref::Server::ND::AdminTrustBypass1 = true;
$Pref::Server::ND::AdminTrustBypass2 = false;
$Pref::Server::ND::SelectPublicBricks = true;
$Pref::Server::ND::MaxBricksAdmin = 1000000;
$Pref::Server::ND::MaxBricksPlayer = 10000;
$Pref::Server::ND::MaxBoxSizeAdmin = 1024;
$Pref::Server::ND::MaxBoxSizePlayer = 64;
$Pref::Server::ND::SelectTimeoutMS = 400;
$Pref::Server::ND::PlantTimeoutMS = 400;
//Advanced
$Pref::Server::ND::PlayMenuSounds = true;
$Pref::Server::ND::MaxGhostBricks = 1500;
$Pref::Server::ND::InstantGhostBricks = 150;
$Pref::Server::ND::ScatterGhostBricks = true;
$Pref::Server::ND::ProcessPerTick = 300;
$Pref::Server::ND::BoxSelectChunkDim = 6;
$Pref::Server::ND::SymTableOnStart = false;
//Always set this to false so we don't accidently reset the prefs
$ND::RestoreDefaultPrefs = false;
}
//Erases outdated prefs from the config file
function ndDeleteOutdatedPrefs()
{
//Step 1: Copy all current prefs
//Limits
%adminOnly = $Pref::Server::ND::AdminOnly;
%paintAdminOnly = $Pref::Server::ND::PaintAdminOnly;
%paintFxAdminOnly = $Pref::Server::ND::PaintFxAdminOnly;
%wrenchAdminOnly = $Pref::Server::ND::WrenchAdminOnly;
%floatAdminOnly = $Pref::Server::ND::FloatAdminOnly;
%saveAdminOnly = $Pref::Server::ND::SaveAdminOnly;
%loadAdminOnly = $Pref::Server::ND::LoadAdminOnly;
%fillBricksAdminOnly = $Pref::Server::ND::FillBricksAdminOnly;
//Settings
%trustLimit = $Pref::Server::ND::TrustLimit;
%adminTrustBypass1 = $Pref::Server::ND::AdminTrustBypass1;
%adminTrustBypass2 = $Pref::Server::ND::AdminTrustBypass2;
%selectPublicBricks = $Pref::Server::ND::SelectPublicBricks;
%maxBricksAdmin = $Pref::Server::ND::MaxBricksAdmin;
%maxBricksPlayer = $Pref::Server::ND::MaxBricksPlayer;
%maxBoxSizeAdmin = $Pref::Server::ND::MaxBoxSizeAdmin;
%maxBoxSizePlayer = $Pref::Server::ND::MaxBoxSizePlayer;
%selectTimeoutMS = $Pref::Server::ND::SelectTimeoutMS;
%plantTimeoutMS = $Pref::Server::ND::PlantTimeoutMS;
//Advanced
%playMenuSounds = $Pref::Server::ND::PlayMenuSounds;
%maxGhostBricks = $Pref::Server::ND::MaxGhostBricks;
%instantGhostBricks = $Pref::Server::ND::InstantGhostBricks;
%scatterGhostBricks = $Pref::Server::ND::ScatterGhostBricks;
%processPerTick = $Pref::Server::ND::ProcessPerTick;
%boxSelectChunkDim = $Pref::Server::ND::BoxSelectChunkDim;
%symTableOnStart = $Pref::Server::ND::SymTableOnStart;
//Step 2: Delete everything
deleteVariables("$Pref::Server::ND::*");
//Step 3: Set current prefs again
//Limits
$Pref::Server::ND::AdminOnly = %adminOnly;
$Pref::Server::ND::PaintAdminOnly = %paintAdminOnly;
$Pref::Server::ND::PaintFxAdminOnly = %paintFxAdminOnly;
$Pref::Server::ND::WrenchAdminOnly = %wrenchAdminOnly;
$Pref::Server::ND::FloatAdminOnly = %floatAdminOnly;
$Pref::Server::ND::SaveAdminOnly = %saveAdminOnly;
$Pref::Server::ND::LoadAdminOnly = %loadAdminOnly;
$Pref::Server::ND::FillBricksAdminOnly = %fillBricksAdminOnl;
//Settings
$Pref::Server::ND::TrustLimit = %trustLimit;
$Pref::Server::ND::AdminTrustBypass1 = %adminTrustBypass1;
$Pref::Server::ND::AdminTrustBypass2 = %adminTrustBypass2;
$Pref::Server::ND::SelectPublicBricks = %selectPublicBricks;
$Pref::Server::ND::MaxBricksAdmin = %maxBricksAdmin;
$Pref::Server::ND::MaxBricksPlayer = %maxBricksPlayer;
$Pref::Server::ND::MaxBoxSizeAdmin = %maxBoxSizeAdmin;
$Pref::Server::ND::MaxBoxSizePlayer = %maxBoxSizePlayer;
$Pref::Server::ND::SelectTimeoutMS = %selectTimeoutMS;
$Pref::Server::ND::PlantTimeoutMS = %plantTimeoutMS;
//Advanced
$Pref::Server::ND::PlayMenuSounds = %playMenuSounds;
$Pref::Server::ND::MaxGhostBricks = %maxGhostBricks;
$Pref::Server::ND::InstantGhostBricks = %instantGhostBricks;
$Pref::Server::ND::ScatterGhostBricks = %scatterGhostBricks;
$Pref::Server::ND::ProcessPerTick = %processPerTick;
$Pref::Server::ND::BoxSelectChunkDim = %boxSelectChunkDim;
$Pref::Server::ND::SymTableOnStart = %symTableOnStart;
}

View File

@ -0,0 +1,180 @@
// Manually sets up symmetry planes for certain bricks with bad geometry.
// -------------------------------------------------------------------
//Manual symmetry can be set using the following variables:
// $ND::ManualSymmetry[UIName] = {0 - 5}
// $ND::ManualSymmetryDB[UIName] = Other UIName
// $ND::ManualSymmetryOffset[UIName] = {0 - 3}
// $ND::ManualSymmetryZ[UIName] = {true, false}
// $ND::ManualSymmetryZDB[UIName] = Other UIName
// $ND::ManualSymmetryZOffset[UIName] = {0 - 3}
//Built-in Bricks
$ND::ManualSymmetryZ["1x1 Round"] = true;
$ND::ManualSymmetryZ["1x1F Round"] = true;
$ND::ManualSymmetryZ["Castle Wall"] = true;
$ND::ManualSymmetryZ["1x4x5 Window"] = true;
//Brick_V15
$ND::ManualSymmetry["1x4x2 Bars"] = 1;
$ND::ManualSymmetryZ["1x4x2 Bars"] = true;
//Brick_Treasure_Chest
$ND::ManualSymmetry["Treasure Chest"] = 2;
//Brick_Teledoor
$ND::ManualSymmetryZ["Teledoor"] = 1;
//Brick_Halloween
$ND::ManualSymmetry["Skull Cool Open"] = 2;
$ND::ManualSymmetry["Skull Cool"] = 2;
$ND::ManualSymmetry["Pumpkin"] = 3;
$ND::ManualSymmetry["Pumpkin_Face"] = 3;
$ND::ManualSymmetry["Pumpkin_Scared"] = 3;
$ND::ManualSymmetry["Pumpkin_Ascii"] = 3;
//Brick_PoleAdapters
$ND::ManualSymmetry["1x1x3 Pole"] = 1;
$ND::ManualSymmetry["1x1 Pole"] = 1;
$ND::ManualSymmetry["1x1F Pole"] = 1;
$ND::ManualSymmetry["1x1F Pole Plus"] = 2;
$ND::ManualSymmetry["1x1F Pole Corner"] = 5;
$ND::ManualSymmetry["1x1F Pole Corner up"] = 2;
$ND::ManualSymmetry["1x1F Pole Corner down"] = 2;
$ND::ManualSymmetry["1x1F Pole T"] = 5;
$ND::ManualSymmetry["1x1F Pole T up"] = 2;
$ND::ManualSymmetry["1x1F Pole T down"] = 2;
$ND::ManualSymmetry["1x1F Pole X Vert"] = 2;
$ND::ManualSymmetry["1x1F Pole X"] = 1;
$ND::ManualSymmetryZ["1x1F Pole Plus"] = true;
$ND::ManualSymmetryZ["1x1F Pole Corner"] = true;
$ND::ManualSymmetryZ["1x1F Pole Corner up"] = false;
$ND::ManualSymmetryZ["1x1F Pole Corner down"] = false;
$ND::ManualSymmetryZ["1x1F Pole T"] = true;
$ND::ManualSymmetryZ["1x1F Pole T up"] = false;
$ND::ManualSymmetryZ["1x1F Pole T down"] = false;
$ND::ManualSymmetryZ["1x1F Pole X Vert"] = true;
$ND::ManualSymmetryZ["1x1F Pole X"] = true;
$ND::ManualSymmetryZDB["1x1F Pole Corner up"] = "1x1F Pole Corner down";
$ND::ManualSymmetryZDB["1x1F Pole Corner down"] = "1x1F Pole Corner up";
$ND::ManualSymmetryZDB["1x1F Pole T up"] = "1x1F Pole T down";
$ND::ManualSymmetryZDB["1x1F Pole T down"] = "1x1F Pole T up";
$ND::ManualSymmetryZOffset["1x1F Pole Corner up"] = 0;
$ND::ManualSymmetryZOffset["1x1F Pole Corner down"] = 0;
$ND::ManualSymmetryZOffset["1x1F Pole T up"] = 0;
$ND::ManualSymmetryZOffset["1x1F Pole T down"] = 0;
//Brick_PoleDiagonals
$ND::ManualSymmetryZ["1x1f Horiz. Diag."] = true;
$ND::ManualSymmetryZ["2x2f Horiz. Diag."] = true;
$ND::ManualSymmetryZ["3x3f Horiz. Diag."] = true;
$ND::ManualSymmetryZ["4x4f Horiz. Diag."] = true;
$ND::ManualSymmetryZ["5x5f Horiz. Diag."] = true;
$ND::ManualSymmetryZ["6x6f Horiz. Diag."] = true;
$ND::ManualSymmetry["1x1 Vert. Diag. A"] = 2;
$ND::ManualSymmetry["2x2 Vert. Diag. A"] = 2;
$ND::ManualSymmetry["3x3 Vert. Diag. A"] = 2;
$ND::ManualSymmetry["4x4 Vert. Diag. A"] = 2;
$ND::ManualSymmetry["5x5 Vert. Diag. A"] = 2;
$ND::ManualSymmetry["6x6 Vert. Diag. A"] = 2;
$ND::ManualSymmetry["1x1 Vert. Diag. B"] = 2;
$ND::ManualSymmetry["2x2 Vert. Diag. B"] = 2;
$ND::ManualSymmetry["3x3 Vert. Diag. B"] = 2;
$ND::ManualSymmetry["4x4 Vert. Diag. B"] = 2;
$ND::ManualSymmetry["5x5 Vert. Diag. B"] = 2;
$ND::ManualSymmetry["6x6 Vert. Diag. B"] = 2;
$ND::ManualSymmetryZ["1x1 Vert. Diag. A"] = false;
$ND::ManualSymmetryZ["2x2 Vert. Diag. A"] = false;
$ND::ManualSymmetryZ["3x3 Vert. Diag. A"] = false;
$ND::ManualSymmetryZ["4x4 Vert. Diag. A"] = false;
$ND::ManualSymmetryZ["5x5 Vert. Diag. A"] = false;
$ND::ManualSymmetryZ["6x6 Vert. Diag. A"] = false;
$ND::ManualSymmetryZ["1x1 Vert. Diag. B"] = false;
$ND::ManualSymmetryZ["2x2 Vert. Diag. B"] = false;
$ND::ManualSymmetryZ["3x3 Vert. Diag. B"] = false;
$ND::ManualSymmetryZ["4x4 Vert. Diag. B"] = false;
$ND::ManualSymmetryZ["5x5 Vert. Diag. B"] = false;
$ND::ManualSymmetryZ["6x6 Vert. Diag. B"] = false;
$ND::ManualSymmetryZDB["1x1 Vert. Diag. A"] = "1x1 Vert. Diag. B";
$ND::ManualSymmetryZDB["2x2 Vert. Diag. A"] = "2x2 Vert. Diag. B";
$ND::ManualSymmetryZDB["3x3 Vert. Diag. A"] = "3x3 Vert. Diag. B";
$ND::ManualSymmetryZDB["4x4 Vert. Diag. A"] = "4x4 Vert. Diag. B";
$ND::ManualSymmetryZDB["5x5 Vert. Diag. A"] = "5x5 Vert. Diag. B";
$ND::ManualSymmetryZDB["6x6 Vert. Diag. A"] = "6x6 Vert. Diag. B";
$ND::ManualSymmetryZDB["1x1 Vert. Diag. B"] = "1x1 Vert. Diag. A";
$ND::ManualSymmetryZDB["2x2 Vert. Diag. B"] = "2x2 Vert. Diag. A";
$ND::ManualSymmetryZDB["3x3 Vert. Diag. B"] = "3x3 Vert. Diag. A";
$ND::ManualSymmetryZDB["4x4 Vert. Diag. B"] = "4x4 Vert. Diag. A";
$ND::ManualSymmetryZDB["5x5 Vert. Diag. B"] = "5x5 Vert. Diag. A";
$ND::ManualSymmetryZDB["6x6 Vert. Diag. B"] = "6x6 Vert. Diag. A";
$ND::ManualSymmetryZOffset["1x1 Vert. Diag. A"] = 2;
$ND::ManualSymmetryZOffset["2x2 Vert. Diag. A"] = 2;
$ND::ManualSymmetryZOffset["3x3 Vert. Diag. A"] = 2;
$ND::ManualSymmetryZOffset["4x4 Vert. Diag. A"] = 2;
$ND::ManualSymmetryZOffset["5x5 Vert. Diag. A"] = 2;
$ND::ManualSymmetryZOffset["6x6 Vert. Diag. A"] = 2;
$ND::ManualSymmetryZOffset["1x1 Vert. Diag. B"] = 2;
$ND::ManualSymmetryZOffset["2x2 Vert. Diag. B"] = 2;
$ND::ManualSymmetryZOffset["3x3 Vert. Diag. B"] = 2;
$ND::ManualSymmetryZOffset["4x4 Vert. Diag. B"] = 2;
$ND::ManualSymmetryZOffset["5x5 Vert. Diag. B"] = 2;
$ND::ManualSymmetryZOffset["6x6 Vert. Diag. B"] = 2;
//Brick_GrillPlate
$ND::ManualSymmetry["Grill Corner"] = 4;
//Brick_Bevel
$ND::ManualSymmetry["1x1F Beveled"] = 1;
$ND::ManualSymmetry["1x1FF Beveled"] = 1;
$ND::ManualSymmetry["1x2F Beveled"] = 1;
$ND::ManualSymmetry["1x2FF Beveled"] = 1;
$ND::ManualSymmetry["1x1 Beveled"] = 1;
$ND::ManualSymmetry["1x1x2 Beveled"] = 1;
$ND::ManualSymmetry["1x2 Beveled"] = 1;
$ND::ManualSymmetry["2x2F Beveled"] = 1;
$ND::ManualSymmetry["2x4F Beveled"] = 1;
$ND::ManualSymmetry["2x4FF Beveled"] = 1;
$ND::ManualSymmetryZ["1x1F Beveled"] = true;
$ND::ManualSymmetryZ["1x1FF Beveled"] = true;
$ND::ManualSymmetryZ["1x2F Beveled"] = true;
$ND::ManualSymmetryZ["1x2FF Beveled"] = true;
$ND::ManualSymmetryZ["1x1 Beveled"] = true;
$ND::ManualSymmetryZ["1x1x2 Beveled"] = true;
$ND::ManualSymmetryZ["1x2 Beveled"] = true;
$ND::ManualSymmetryZ["2x2F Beveled"] = true;
$ND::ManualSymmetryZ["2x4F Beveled"] = true;
$ND::ManualSymmetryZ["2x4FF Beveled"] = true;
//Brick_1RandomPack
$ND::ManualSymmetry["2x2x2 Octo Elbow Horz"] = 4;
$ND::ManualSymmetryZ["1x1 Octo"] = true;
$ND::ManualSymmetryZ["1x1x2 Octo"] = true;
$ND::ManualSymmetryZ["2x2x2 Octo T Horz"] = true;
$ND::ManualSymmetryZ["2x2x2 Octo Elbow Horz"] = true;
$ND::ManualSymmetryZ["2x3x2 Octo Offset"] = false;
$ND::ManualSymmetryZDB["2x3x2 Octo Offset"] = "2x3x2 Octo Offset";
$ND::ManualSymmetryZOffset["2x3x2 Octo Offset"] = 2;
//Brick_Fence
$ND::ManualSymmetry["1x4 Fence"] = 3;
//Brick_SmallBricks
$ND::ManualSymmetry["0.25x0.25 Corner"] = 4;
$ND::ManualSymmetry["0.25x0.25F Corner"] = 4;
$ND::ManualSymmetry["0.5x0.5 Corner"] = 4;
$ND::ManualSymmetry["0.5x0.5F Corner"] = 4;
$ND::ManualSymmetry["0.75x0.75 Corner"] = 4;
$ND::ManualSymmetry["0.75x0.75F Corner"] = 4;

View File

@ -0,0 +1,692 @@
// Analyzes brick geometry to detect common symmetry planes.
// -------------------------------------------------------------------
//Begin generating the symmetry table
function ndCreateSymmetryTable()
{
if($ND::SymmetryTableCreating)
return;
//Tell everyone what is happening
messageAll('', "\c6(\c3New Duplicator\c6) \c6Creating brick symmetry table...");
//Make sure we have the uiname table for manual symmetry
if(!$UINameTableCreated)
createUINameTable();
//Delete previous data
deleteVariables("$ND::Symmetry*");
$ND::SymmetryTableCreated = false;
$ND::SymmetryTableCreating = true;
$ND::SymmetryTableStarted = getRealTime();
$NDT::SimpleCount = 0;
$NDT::MeshCount = 0;
$NDT::AsymXCountTotal = 0;
$NDT::AsymZCountTotal = 0;
echo("ND: Start building brick symmetry table...");
echo("==========================================================================");
ndTickCreateSymmetryTable(0, getDatablockGroupSize());
}
//Process symmetry for 6 datablocks each tick
function ndTickCreateSymmetryTable(%lastIndex, %max)
{
%processed = 0;
%limit = $Server::Dedicated ? 400 : 200;
for(%i = %lastIndex; %i < %max; %i++)
{
%db = getDatablock(%i);
if(%db.getClassName() $= "FxDtsBrickData")
{
%processed += ndTestBrickSymmetry(%db);
if(%processed > %limit)
{
schedule(30, 0, ndTickCreateSymmetryTable, %i + 1, %max);
return;
}
}
}
%simple = $NDT::SimpleCount;
%mesh = $NDT::MeshCount;
%asymx = $NDT::AsymXCountTotal;
%asymz = $NDT::AsymZCountTotal;
echo("==========================================================================");
echo("ND: Finished basic symmetry tests: " @ %simple @ " simple, " @ %mesh @ " with mesh, " @ %asymx @ " asymmetric, " @ %asymz @ " z-asymmetric");
ndFindSymmetricPairs();
}
//Attempt to find symmetric pairs between asymmetric bricks
function ndFindSymmetricPairs()
{
echo("ND: Starting X symmetric pair search...");
echo("==========================================================================");
for(%i = 0; %i < $NDT::AsymXCountTotal; %i++)
{
%index = $NDT::AsymXBrick[%i];
if(!$NDT::SkipAsymX[%index])
ndFindSymmetricPairX(%index);
}
echo("==========================================================================");
echo("ND: Finished finding symmetric pairs");
echo("ND: Starting Z symmetric pair search...");
echo("==========================================================================");
for(%i = 0; %i < $NDT::AsymZCountTotal; %i++)
{
%index = $NDT::AsymZBrick[%i];
if(!$NDT::SkipAsymZ[%index])
ndFindSymmetricPairZ(%index);
}
//Delete temporary arrays
deleteVariables("$NDT::*");
echo("==========================================================================");
echo("ND: Finished finding Z symmetric pairs");
echo("ND: Symmetry table complete in " @ (getRealTime() - $ND::SymmetryTableStarted) / 1000 @ " seconds");
$ND::SymmetryTableCreated = true;
$ND::SymmetryTableCreating = false;
//We're done!
%seconds = mFloatLength((getRealTime() - $ND::SymmetryTableStarted) / 1000, 0);
messageAll('', "\c6(\c3New Duplicator\c6) \c6Created brick symmetry table in " @ %seconds @ " seconds.");
}
//Test symmetry of a single blb file
function ndTestBrickSymmetry(%datablock)
{
//Open blb file
%file = new FileObject();
%file.openForRead(%datablock.brickFile);
//Skip brick size - irrelevant
%file.readLine();
//Simple bricks are always fully symmetric
if(%file.readLine() $= "BRICK")
{
$NDT::SimpleCount++;
$ND::Symmetry[%datablock] = 1;
$ND::SymmetryZ[%datablock] = true;
%file.close();
%file.delete();
return 2;
}
//Not simple, get mesh data index in temp arrays
%dbi = $NDT::MeshCount;
$NDT::MeshCount++;
//Load mesh from blb file
%faces = 0;
%points = 0;
while(!%file.isEOF())
{
//Find start of face
%line = %file.readLine();
if(getSubStr(%line, 0, 4) $= "TEX:")
{
%tex = trim(getSubStr(%line, 4, strLen(%line)));
//Top and bottom faces have different topology, skip
if(%tex $= "TOP" || %tex $= "BOTTOMLOOP" || %tex $= "BOTTOMEDGE")
continue;
//Add face
$NDT::FaceTexId[%dbi, %faces] = (%tex $= "SIDE" ? 0 : (%tex $= "RAMP" ? 1 : 2));
//Skip useless lines
while(trim(%file.readLine()) !$= "POSITION:") {}
//Add the 4 points
for(%i = 0; %i < 4; %i++)
{
//Read next line
%line = %file.readLine();
//Skip useless blank lines
while(!strLen(%line))
%line = %file.readLine();
//Remove formatting from point
%pos = vectorAdd(%line, "0 0 0");
//Round down two digits to fix float errors
%pos = mFloatLength(getWord(%pos, 0), 3) * 1.0
SPC mFloatLength(getWord(%pos, 1), 3) * 1.0
SPC mFloatLength(getWord(%pos, 2), 3) * 1.0;
//Get index of this point
if(!%ptIndex = $NDT::PtAtPosition[%dbi, %pos])
{
//Points array is 1-indexed so we can quickly test !PtAtPosition[...]
%points++;
%ptIndex = %points;
//Add new point to array
$NDT::PtPosition[%dbi, %points] = %pos;
$NDT::PtAtPosition[%dbi, %pos] = %points;
}
//Add face to point
if(!$NDT::PtInFace[%dbi, %faces, %ptIndex])
{
//Increase first then subtract 1 to get 0 the first time
%fIndex = $NDT::FacesAtPt[%dbi, %ptIndex]++ - 1;
$NDT::FaceAtPt[%dbi, %ptIndex, %fIndex] = %faces;
}
//Add point to face
$NDT::FacePt[%dbi, %faces, %i] = %ptIndex;
$NDT::PtInFace[%dbi, %faces, %ptIndex] = true;
}
//Added face
%faces++;
}
}
$NDT::FaceCount[%dbi] = %faces;
$NDT::Datablock[%dbi] = %datablock;
%file.close();
%file.delete();
//Possible symmetries:
// 0: asymmetric
// 1: x & y
// 2: x
// 3: y
// 4: x+y
// 5: x-y
//We will test in the following order:
// X
// Y
// X+Y
// X-Y
// Z
//Check manual symmetry first
%sym = $ND::ManualSymmetry[%datablock.uiname];
if(%sym !$= "")
{
if(!%sym)
{
//Try to find the other brick
%otherdb = $UINameTable[$ND::ManualSymmetryDB[%datablock.uiname]];
%offset = $ND::ManualSymmetryOffset[%datablock.uiname];
//...
if(!isObject(%otherdb))
{
%otherdb = "";
%offset = 0;
echo("ND: " @ %datablock.uiname @ " has manual symmetry but the paired brick does not exist");
}
$ND::SymmetryXDatablock[%datablock] = %otherdb;
$ND::SymmetryXOffset[%datablock] = %offset;
}
%manualSym = true;
}
else
{
%failX = ndTestSelfSymmetry(%dbi, 0);
%failY = ndTestSelfSymmetry(%dbi, 1);
//Diagonals are only needed if the brick isn't symmetric to the axis
if(%failX && %failY)
%failXY = ndTestSelfSymmetry(%dbi, 3);
//One diagonal is enough, only test second if first one fails
if(%failXY)
%failYX = ndTestSelfSymmetry(%dbi, 4);
//X, Y symmetry
if(!%failX && !%failY)
%sym = 1;
else if(!%failX)
%sym = 2;
else if(!%failY)
%sym = 3;
else if(!%failXY)
%sym = 4;
else if(!%failYX)
%sym = 5;
else
%sym = 0;
}
//Check manual symmetry first
%symZ = $ND::ManualSymmetryZ[%datablock.uiname];
//Z symmetry
if(%symZ !$= "")
{
if(!%symZ)
{
//Try to find the other brick
%otherdb = $UINameTable[$ND::ManualSymmetryZDB[%datablock.uiname]];
%offset = $ND::ManualSymmetryZOffset[%datablock.uiname];
//...
if(!isObject(%otherdb))
{
%otherdb = "";
%offset = 0;
echo("ND: " @ %datablock.uiname @ " has manual Z symmetry but the paired brick does not exist");
}
$ND::SymmetryZDatablock[%datablock] = %otherdb;
$ND::SymmetryZOffset[%datablock] = %offset;
}
%manualZSym = true;
}
else
%symZ = !ndTestSelfSymmetry(%dbi, 2);
if(!%manualSym && !%sym)
{
//Add to lookup table of X-asymmetric bricks of this type
%bIndex = $NDT::AsymXCount[%faces, %symZ]++;
$NDT::AsymXBrick[%faces, %symZ, %bIndex] = %dbi;
//Add to list of asymmetric bricks
$NDT::AsymXBrick[$NDT::AsymXCountTotal] = %dbi;
$NDT::AsymXCountTotal++;
}
if(!%manualZSym && !%symZ)
{
//Add to lookup table of Z-asymmetric bricks of this type
%bIndex = $NDT::AsymZCount[%faces, %sym]++;
$NDT::AsymZBrick[%faces, %sym, %bIndex] = %dbi;
//Add to list of Z-asymmetric bricks
$NDT::AsymZBrick[$NDT::AsymZCountTotal] = %dbi;
$NDT::AsymZCountTotal++;
}
//Save symmetries
$ND::Symmetry[%datablock] = %sym;
$ND::SymmetryZ[%datablock] = %symZ;
//Return processed faces
return %faces;
}
//Find symmetric pair between two bricks on X axis
function ndFindSymmetricPairX(%dbi)
{
if($NDT::SkipAsymX[%dbi])
return;
%datablock = $NDT::Datablock[%dbi];
%zsym = $ND::SymmetryZ[%datablock];
%faces = $NDT::FaceCount[%dbi];
%count = $NDT::AsymXCount[%faces, %zsym];
//Only potential match is the brick itself - fail
if(%count == 1)
{
echo("ND: No X match for " @ %datablock.getName() @ " (" @ %datablock.category @ "/" @ %datablock.subCategory @ "/" @ %datablock.uiname @ ")");
return;
}
%off = -1;
$NDT::SkipAsymX[%dbi] = true;
for(%i = 1; %i <= %count; %i++)
{
%other = $NDT::AsymXBrick[%faces, %zsym, %i];
//Don't compare with bricks that already have a pair
if($NDT::SkipAsymX[%other])
continue;
//Test all 4 possible rotations
//Not using loop due to lack of goto command
if(!ndTestPairSymmetry(%dbi, %other, true, 0))
{
%off = 0;
break;
}
if(!ndTestPairSymmetry(%dbi, %other, true, 1))
{
%off = 1;
break;
}
if(!ndTestPairSymmetry(%dbi, %other, true, 2))
{
%off = 2;
break;
}
if(!ndTestPairSymmetry(%dbi, %other, true, 3))
{
%off = 3;
break;
}
}
if(%off != -1)
{
%otherdb = $NDT::Datablock[%other];
//Save symmetry
$ND::SymmetryXDatablock[%datablock] = %otherdb;
$ND::SymmetryXOffset[%datablock] = %off;
$ND::SymmetryXDatablock[%otherdb] = %datablock;
$ND::SymmetryXOffset[%otherdb] = -%off;
//No need to process the other brick again
$NDT::SkipAsymX[%other] = true;
}
else
echo("ND: No X match for " @ %datablock.getName() @ " (" @ %datablock.category @ "/" @ %datablock.subCategory @ "/" @ %datablock.uiname @ ")");
}
//Find symmetric pair between two bricks on Z axis
function ndFindSymmetricPairZ(%dbi)
{
if($NDT::SkipAsymZ[%dbi])
return;
%datablock = $NDT::Datablock[%dbi];
%sym = $ND::Symmetry[%datablock];
%faces = $NDT::FaceCount[%dbi];
%count = $NDT::AsymZCount[%faces, %sym];
//Only potential match is the brick itself - fail
if(%count == 1)
{
echo("ND: No Z match for " @ %datablock.getName() @ " (" @ %datablock.category @ "/" @ %datablock.subCategory @ "/" @ %datablock.uiname @ ")");
return;
}
%off = -1;
for(%i = 1; %i <= %count; %i++)
{
%other = $NDT::AsymZBrick[%faces, %sym, %i];
//Don't compare with bricks that already have a pair
if($NDT::SkipAsymZ[%other])
continue;
//Test all 4 possible rotations
//Not using loop due to lack of goto command
if(!ndTestPairSymmetry(%dbi, %other, false, 0))
{
%off = 0;
break;
}
if(!ndTestPairSymmetry(%dbi, %other, false, 1))
{
%off = 1;
break;
}
if(!ndTestPairSymmetry(%dbi, %other, false, 2))
{
%off = 2;
break;
}
if(!ndTestPairSymmetry(%dbi, %other, false, 3))
{
%off = 3;
break;
}
}
//It's possible for a brick to match itself rotated
//here, so only mark it after the search
$NDT::SkipAsymZ[%dbi] = true;
if(%off != -1)
{
%otherdb = $NDT::Datablock[%other];
//Save symmetry
$ND::SymmetryZDatablock[%datablock] = %otherdb;
$ND::SymmetryZOffset[%datablock] = %off;
$ND::SymmetryZDatablock[%otherdb] = %datablock;
$ND::SymmetryZOffset[%otherdb] = -%off;
//No need to process the other brick again
$NDT::SkipAsymZ[%other] = true;
}
else
echo("ND: No Z match for " @ %datablock.getName() @ " (" @ %datablock.category @ "/" @ %datablock.subCategory @ "/" @ %datablock.uiname @ ")");
}
//Test a mesh for a single symmetry plane in itself
function ndTestSelfSymmetry(%dbi, %plane)
{
%fail = false;
%faces = $NDT::FaceCount[%dbi];
for(%i = 0; %i < %faces; %i++)
{
//If this face was already used by another mirror, skip
if(%skipFace[%i])
continue;
//Attempt to find the mirrored points
for(%j = 0; %j < 4; %j++)
{
%pt = $NDT::FacePt[%dbi, %i, %j];
//Do we already know the mirrored one?
if(%mirrPt[%pt])
{
%mirr[%j] = %mirrPt[%pt];
continue;
}
//Get position of point
%v = $NDT::PtPosition[%dbi, %pt];
//Get point at mirrored position based on plane
switch$(%plane)
{
//Flip X
case 0: %mirr = $NDT::PtAtPosition[%dbi, -firstWord(%v) SPC restWords(%v)];
//Flip Y
case 1: %mirr = $NDT::PtAtPosition[%dbi, getWord(%v, 0) SPC -getWord(%v, 1) SPC getWord(%v, 2)];
//Flip Z
case 2: %mirr = $NDT::PtAtPosition[%dbi, getWords(%v, 0, 1) SPC -getWord(%v, 2)];
//Mirror along X+Y
case 3: %mirr = $NDT::PtAtPosition[%dbi, -getWord(%v, 1) SPC -getWord(%v, 0) SPC getWord(%v, 2)];
//Mirror along X-Y
default: %mirr = $NDT::PtAtPosition[%dbi, getWord(%v, 1) SPC getWord(%v, 0) SPC getWord(%v, 2)];
}
if(%mirr)
{
%mirrPt[%pt] = %mirr;
%mirrPt[%mirr] = %pt;
%mirr[%j] = %mirr;
}
else
{
%fail = true;
break;
}
}
if(%fail)
break;
//Test whether the points have a common face
%fail = true;
%count = $NDT::FacesAtPt[%dbi, %mirr0];
for(%j = 0; %j < %count; %j++)
{
%potentialFace = $NDT::FaceAtPt[%dbi, %mirr0, %j];
//Mirrored face must have the same texture id
if($NDT::FaceTexId[%dbi, %i] != $NDT::FaceTexId[%dbi, %potentialFace])
continue;
//Check whether remaining points are in the face
if(!$NDT::PtInFace[%dbi, %potentialFace, %mirr1])
continue;
if(!$NDT::PtInFace[%dbi, %potentialFace, %mirr2])
continue;
if(!$NDT::PtInFace[%dbi, %potentialFace, %mirr3])
continue;
//We found a matching face!
%skipFace[%potentialFace] = true;
%fail = false;
break;
}
if(%fail)
break;
}
return %fail;
}
//Test X or Z symmetry between two meshes with rotation offset
function ndTestPairSymmetry(%dbi, %other, %plane, %rotation)
{
%fail = false;
%faces = $NDT::FaceCount[%dbi];
for(%i = 0; %i < %faces; %i++)
{
//Attempt to find the mirrored points
for(%j = 0; %j < 4; %j++)
{
%pt = $NDT::FacePt[%dbi, %i, %j];
//Do we already know the mirrored one?
if(%mirrPt[%pt])
{
%mirr[%j] = %mirrPt[%pt];
continue;
}
//Get position of point
%v = $NDT::PtPosition[%dbi, %pt];
//true = X, false = Z
if(%plane)
{
//Get point at mirrored position based on rotation
switch(%rotation)
{
//Flip X
case 0: %mirr = $NDT::PtAtPosition[%other, -firstWord(%v) SPC restWords(%v)];
//Flip X, rotate 90
case 1: %mirr = $NDT::PtAtPosition[%other, getWord(%v, 1) SPC getWord(%v, 0) SPC getWord(%v, 2)];
//Flip X, rotate 180
case 2: %mirr = $NDT::PtAtPosition[%other, getWord(%v, 0) SPC -getWord(%v, 1) SPC getWord(%v, 2)];
//Flip X, rotate 270
default: %mirr = $NDT::PtAtPosition[%other, -getWord(%v, 1) SPC -getWord(%v, 0) SPC getWord(%v, 2)];
}
}
else
{
//Get point at mirrored position based on rotation
switch(%rotation)
{
//Flip Z
case 0: %mirr = $NDT::PtAtPosition[%other, getWord(%v, 0) SPC getWord(%v, 1) SPC -getWord(%v, 2)];
//Flip Z, rotate 90
case 1: %mirr = $NDT::PtAtPosition[%other, getWord(%v, 1) SPC -getWord(%v, 0) SPC -getWord(%v, 2)];
//Flip Z, rotate 180
case 2: %mirr = $NDT::PtAtPosition[%other, -getWord(%v, 0) SPC -getWord(%v, 1) SPC -getWord(%v, 2)];
//Flip Z, rotate 270
default: %mirr = $NDT::PtAtPosition[%other, -getWord(%v, 1) SPC getWord(%v, 0) SPC -getWord(%v, 2)];
}
}
if(%mirr)
{
%mirrPt[%pt] = %mirr;
%mirr[%j] = %mirr;
}
else
{
%fail = true;
break;
}
}
if(%fail)
break;
//Test whether the points have a common face
%fail = true;
%count = $NDT::FacesAtPt[%other, %mirr0];
for(%j = 0; %j < %count; %j++)
{
%potentialFace = $NDT::FaceAtPt[%other, %mirr0, %j];
//Mirrored face must have the same texture id
if($NDT::FaceTexId[%dbi, %i] != $NDT::FaceTexId[%other, %potentialFace])
continue;
//Check whether remaining points are in the face
if(!$NDT::PtInFace[%other, %potentialFace, %mirr1])
continue;
if(!$NDT::PtInFace[%other, %potentialFace, %mirr2])
continue;
if(!$NDT::PtInFace[%other, %potentialFace, %mirr3])
continue;
//We found a matching face!
%fail = false;
break;
}
if(%fail)
break;
}
return %fail;
}

31
scripts/server/undo.cs Normal file
View File

@ -0,0 +1,31 @@
// Handles the undo stack for duplicator actions.
// -------------------------------------------------------------------
package NewDuplicator_Server
{
//Catch things falling off the end of the undo stack
function QueueSO::push(%obj, %val)
{
%lastVal = %obj.val[(%obj.head + 1) % %obj.size];
if(getFieldCount(%lastVal) == 2)
{
%str = getField(%lastVal, 1);
if(
%str $= "ND_PLANT"
|| %str $= "ND_PAINT"
|| %str $= "ND_WRENCH"
){
%qobj = getField(%lastVal, 0);
if(isObject(%qobj)){
%qobj.delete();
}else{
// talk("QueueSO::push(" @ %obj @ ", " @ %val @ ") - Nonexistent object " @ %qobj);
}
}
}
parent::push(%obj, %val);
}
};

65
server.cs Normal file
View File

@ -0,0 +1,65 @@
// Executes all required scripts and initializes the server side.
// -------------------------------------------------------------------
$ND::Version = "1.6.2";
$ND::FilePath = filePath($Con::File) @ "/";
$ND::ConfigPath = "config/NewDuplicator/";
$ND::ClassPath = $ND::FilePath @ "classes/";
$ND::ScriptPath = $ND::FilePath @ "scripts/";
$ND::ResourcePath = $ND::FilePath @ "resources/";
if(isObject(ND_ServerGroup))
ND_ServerGroup.delete();
new ScriptGroup(ND_ServerGroup);
exec($ND::ClassPath @ "server/ghostgroup.cs");
exec($ND::ClassPath @ "server/highlightbox.cs");
exec($ND::ClassPath @ "server/selection.cs");
exec($ND::ClassPath @ "server/selectionbox.cs");
exec($ND::ClassPath @ "server/undogrouppaint.cs");
exec($ND::ClassPath @ "server/undogroupplant.cs");
exec($ND::ClassPath @ "server/undogroupwrench.cs");
exec($ND::ClassPath @ "server/duplimode/boxselect.cs");
exec($ND::ClassPath @ "server/duplimode/boxselectprogress.cs");
exec($ND::ClassPath @ "server/duplimode/cutprogress.cs");
exec($ND::ClassPath @ "server/duplimode/fillcolor.cs");
exec($ND::ClassPath @ "server/duplimode/fillcolorprogress.cs");
exec($ND::ClassPath @ "server/duplimode/loadprogress.cs");
exec($ND::ClassPath @ "server/duplimode/plantcopy.cs");
exec($ND::ClassPath @ "server/duplimode/plantcopyprogress.cs");
exec($ND::ClassPath @ "server/duplimode/saveprogress.cs");
exec($ND::ClassPath @ "server/duplimode/stackselect.cs");
exec($ND::ClassPath @ "server/duplimode/stackselectprogress.cs");
exec($ND::ClassPath @ "server/duplimode/supercutprogress.cs");
exec($ND::ClassPath @ "server/duplimode/wrenchprogress.cs");
exec($ND::ScriptPath @ "common/bytetable.cs");
exec($ND::ScriptPath @ "server/commands.cs");
exec($ND::ScriptPath @ "server/datablocks.cs");
exec($ND::ScriptPath @ "server/functions.cs");
exec($ND::ScriptPath @ "server/handshake.cs");
exec($ND::ScriptPath @ "server/highlight.cs");
exec($ND::ScriptPath @ "server/images.cs");
exec($ND::ScriptPath @ "server/modes.cs");
exec($ND::ScriptPath @ "server/namedtargets.cs");
exec($ND::ScriptPath @ "server/prefs.cs");
exec($ND::ScriptPath @ "server/symmetrydefinitions.cs");
exec($ND::ScriptPath @ "server/symmetrytable.cs");
exec($ND::ScriptPath @ "server/undo.cs");
activatePackage(NewDuplicator_Server);
schedule(10, 0, activatePackage, NewDuplicator_Server_Final);
ndRegisterDuplicatorModes();
ndRegisterPrefs();
ndResendHandshakes();
if($Pref::Server::ND::SymTableOnStart && !$ND::SymmetryTableCreated){
schedule(10, 0, ndCreateSymmetryTable);
}
exec("./v20fix.cs");

15
v20fix.cs Normal file
View File

@ -0,0 +1,15 @@
function colorFToI(%f){
%i = %f*255;
%i = mFloor(%i + 0.5);
return %i;
}
function getColorI(%color){
%color2 =
colorFToI(getWord(%color, 0)) SPC
colorFToI(getWord(%color, 1)) SPC
colorFToI(getWord(%color, 2)) SPC
colorFToI(getWord(%color, 3))
;
}