diff --git a/classes/server/duplimode/boxselect.cs b/classes/server/duplimode/boxselect.cs index 8eadd50..629827a 100644 --- a/classes/server/duplimode/boxselect.cs +++ b/classes/server/duplimode/boxselect.cs @@ -1,4 +1,3 @@ -//bls 4 // This file should not exist. Fix later... // ------------------------------------------------------------------- @@ -61,6 +60,8 @@ function NDM_BoxSelect::onStartMode(%this, %client, %lastMode) //Switch away from this mode function NDM_BoxSelect::onChangeMode(%this, %client, %nextMode) { + %client.ndBoxCleared(); + if(%nextMode == $NDM::StackSelect) { //Clear selection @@ -107,6 +108,8 @@ function NDM_BoxSelect::onChangeMode(%this, %client, %nextMode) //Kill this mode function NDM_BoxSelect::onKillMode(%this, %client) { + %client.ndBoxCleared(); + //Destroy selection if(isObject(%client.ndSelection)) %client.ndSelection.delete(); @@ -164,6 +167,7 @@ function NDM_BoxSelect::onSelectObject(%this, %client, %obj, %pos, %normal) else { %client.ndSelectionBox.zoneBrick = ""; + %client.ndSelectionBox.setNormalColors(); %box = %obj.getWorldBox(); %p1 = getWords(%box, 0, 2); %p2 = getWords(%box, 3, 5); @@ -186,6 +190,7 @@ function NDM_BoxSelect::onSelectObject(%this, %client, %obj, %pos, %normal) } else if (isObject(%obj.physicalZone)) { %box = ndGetBoxFromZone(%obj); %client.ndSelectionBox.zoneBrick = %obj; + %client.ndSelectionBox.setZoneColors(); } else { %box = ndGetPlateBoxFromRayCast(%pos, %normal); } @@ -208,7 +213,7 @@ function ndRound(%v, %step) { return mFloor(%v/%step + 0.5)*%step; } -function ndCorrectBox(%box) { +function ndSnapBoxToGrid(%box) { %xl = ndRound(getWord(%box, 0), 0.5); %yl = ndRound(getWord(%box, 1), 0.5); %zl = ndRound(getWord(%box, 2), 0.2); @@ -226,19 +231,16 @@ function ndGetBoxFromRom(%b) { %bh = getWords(%box, 3, 5); %bl = vectorAdd(%bl, "0 0 " SPC (%db.brickSizeZ * 0.2)); %bh = vectorAdd(%bh, "0 0 " SPC (%db.logicRomZ * 0.2)); - return ndCorrectBox(%bl SPC %bh); + return ndSnapBoxToGrid(%bl SPC %bh); } function ndGetBoxFromZone(%b) { %z = %b.physicalZone; %pos = %z.position; %scale = %z.getScale(); - %sx = getWord(%scale, 0); - %sy = getWord(%scale, 1); - %sz = getWord(%scale, 2); - %bl = vectorAdd(%pos, 0 SPC -%sy SPC 0); - %bh = vectorAdd(%pos, %sx SPC 0 SPC %sz); - return ndCorrectBox(%bl SPC %bh); + %bl = vectorAdd(%pos, 0 SPC -%getWord(%scale,1) SPC 0); + %bh = vectorAdd(%pos, getWord(%scale,0) SPC 0 SPC getWord(%scale,2)); + return ndSnapBoxToGrid(%bl SPC %bh); } @@ -366,12 +368,18 @@ function ndApplyZoneEvent(%brick, %p1a, %p2a, %client) { if(%brick.eventOutput[%i] $= "setZoneBox") { %brick.eventOutputParameter[%i, 1] = %p1; %brick.eventOutputParameter[%i, 2] = %p2; + + // update event + %brick.setZoneBox(%p1,%p2, %brick.eventOutputParameter[%i,3], 0); + if(isFunction("fxDtsBrick", "triggerPropertyOnLoad")) + %brick.triggerPropertyOnLoad(); + + // inform client messageClient(%client, 'MsgError', ""); commandToClient(%client, 'centerPrint', "\c6Applied selection box to zone event \c3" @ %i - @ "\c6 on brick, and re-created zone.", 5); - %brick.setZoneBox(%p1, %p2, - %brick.eventOutputParameter[%i,3], 0); + @ "\c6 on brick,
\c6re-created zone, and triggered propertyOnLoad events.", 5); + return; } } diff --git a/classes/server/duplimode/boxselecthistory.cs b/classes/server/duplimode/boxselecthistory.cs index b658935..87508d6 100644 --- a/classes/server/duplimode/boxselecthistory.cs +++ b/classes/server/duplimode/boxselecthistory.cs @@ -16,24 +16,29 @@ function gameConnection::ndCreateSelectionBox(%client) { %client.ndSelectionBox = ND_SelectionBox(%shapeName); } -function gameConnection::ndPushBoxHistory(%client) { - //talk("push"); - if(!isObject(%client.ndSelectionBox)) return; - %box = %client.ndSelectionBox.point1 SPC %client.ndSelectionBox.point2; +function gameConnection::ndPushBoxHistory(%client, %optBox) { + if(%optBox $= "") { + if(!isObject(%client.ndSelectionBox)) return; + %box = %client.ndSelectionBox.point1 SPC %client.ndSelectionBox.point2; + %zone = %client.ndSelectionBox.zoneBrick; + } else { + %box = %optBox; + %zone = ""; + } + %entry = %box TAB %zone; + if(%client.ndBoxHistoryCount>0 && - %box $= %client.ndBoxHistory[%client.ndBoxHistoryCount-1]) + %entry $= %client.ndBoxHistory[%client.ndBoxHistoryCount-1]) return; - %client.ndBoxHistory[%client.ndBoxHistoryCount+0] = %box; + %client.ndBoxHistory[%client.ndBoxHistoryCount+0] = %entry; %client.ndBoxHistoryCount++; %client.ndBoxHistoryPos = 1; // recall prior to this one } function gameConnection::ndBoxChanged(%client) { - //talk("changed"); %client.ndBoxHistoryPos = 0; // recall starting with this initial selection %client.ndSaveBoxOnRecall = true; } function gameConnection::ndBoxCleared(%client) { - //talk("cleared"); if(%client.ndSaveBoxOnRecall) { %client.ndPushBoxHistory(); %client.ndSaveBoxOnRecall = false; @@ -41,18 +46,26 @@ function gameConnection::ndBoxCleared(%client) { %client.ndBoxHistoryPos = 0; } function gameConnection::ndRecallBoxHistory(%client, %offset) { - //talk("recall " @ %offset); if(%offset<1 || %offset>%client.ndBoxHistoryCount) return; %idx = %client.ndBoxHistoryCount - %offset; - %box = %client.ndBoxHistory[%idx]; + %entry = %client.ndBoxHistory[%idx]; + %box = getField(%entry, 0); + %zone = getField(%entry, 1); + %p1 = getWords(%box,0,2); %p2 = getWords(%box,3,5); if(!isObject(%client.ndSelectionBox)) %client.ndCreateSelectionBox(); + %client.ndSelectionBox.zoneBrick = %zone; + if(isObject(%zone)) + %client.ndSelectionBox.setZoneColors(); + else + %client.ndSelectionBox.setNormalColors(); %client.ndSelectionBox.setSize(%p1,%p2); %client.ndUpdateBottomPrint(); + ndBoxPlaySound(%client.ndSelectionBox, BrickMoveSound); } diff --git a/classes/server/duplimode/plantcopy.cs b/classes/server/duplimode/plantcopy.cs index 86ff30d..20458e1 100644 --- a/classes/server/duplimode/plantcopy.cs +++ b/classes/server/duplimode/plantcopy.cs @@ -1,4 +1,3 @@ -//bls 4 // This file should not exist. Fix later... // ------------------------------------------------------------------- @@ -273,6 +272,8 @@ function NDM_PlantCopy::conditionalPlant(%this, %client, %force, %ownership) return; } + %client.ndPushBoxHistory(%client.ndSelection.getGhostWorldBox()); + %client.ndLastPlantTime = $Sim::Time; %pos = %client.ndSelection.ghostPosition; diff --git a/classes/server/duplimode/stackselect.cs b/classes/server/duplimode/stackselect.cs index 9085808..175d3f0 100644 --- a/classes/server/duplimode/stackselect.cs +++ b/classes/server/duplimode/stackselect.cs @@ -1,4 +1,3 @@ -//bls 4 // This file should not exist. Fix later... // ------------------------------------------------------------------- diff --git a/classes/server/selection.cs b/classes/server/selection.cs index 64b130f..bf52b69 100644 --- a/classes/server/selection.cs +++ b/classes/server/selection.cs @@ -1,4 +1,3 @@ -//bls 3 // This file is way too big. Fix later... // ------------------------------------------------------------------- diff --git a/classes/server/selectionbox.cs b/classes/server/selectionbox.cs index 9387bec..9f181da 100644 --- a/classes/server/selectionbox.cs +++ b/classes/server/selectionbox.cs @@ -55,11 +55,27 @@ function ND_SelectionBox::onRemove(%this) %this.border_y[%i].delete(); %this.border_z[%i].delete(); } + + ndShapelineConnectBoxes_delete(%this); } -//Set normal color values and borders -function ND_SelectionBox::setNormalMode(%this) -{ +function ND_SelectionBox::setZoneColors(%this) { + %this.innerColor = "0 0 0 0.60"; + %this.outerColor = "0 0 0 0.35"; + + %this.borderColor = "1 0.7 0.5 0.99"; + %this.borderColorSelected = "0.7 0 1 0.99"; + + %this.cornerColor1 = "0.8 0.74 0.5 0.99"; + %this.cornerColor2 = "1 0.94 0.5 0.99"; + + %this.cornerColorSelected1 = "0.7 0.2 1 0.99"; + %this.cornerColorSelected2 = "0.7 0.1 1 0.99"; + + %this.applyColors(); +} + +function ND_SelectionBox::setNormalColors(%this) { %this.innerColor = "0 0 0 0.60"; %this.outerColor = "0 0 0 0.35"; @@ -71,7 +87,13 @@ function ND_SelectionBox::setNormalMode(%this) %this.cornerColorSelected1 = "0 0.2 1 0.99"; %this.cornerColorSelected2 = "0 0.1 0.9 0.99"; + + %this.applyColors(); +} +//Set normal color values and borders +function ND_SelectionBox::setNormalMode(%this) +{ %this.isNormalMode = true; //Unhide the corners and inner/outer box (hidden in disabled mode) @@ -83,7 +105,7 @@ function ND_SelectionBox::setNormalMode(%this) %this.outerBox.unHideNode("ALL"); //Apply changes - %this.applyColors(); + %this.setNormalColors(); %this.setSize(%this.point1, %this.point2); %this.shapeName.setShapeName(%this.boxName); } @@ -307,6 +329,16 @@ function ND_SelectionBox::setSize(%this, %point1, %point2) %this.border_z[%i].setScale(%width SPC %width SPC %len_z + %width * 0.05); } } + + // Draw lines from zone brick to zone if present + if(isObject(%this.zoneBrick)) { + ndShapelineConnectBoxes_create(%this, "1 0.7 0.5 0.3"); + ndShapelineConnectBoxes_move(%this, + %this.zoneBrick.getWorldBox(), + %this.point1 SPC %this.point2); + } else { + ndShapelineConnectBoxes_delete(%this); + } } //Resize the selection box and align it to a player diff --git a/readme.md b/readme.md index be52494..4658cd0 100644 --- a/readme.md +++ b/readme.md @@ -5,18 +5,18 @@ A fork of the original New Duplicator by Zeblote, presently maintained and with - Added the `/ownership` or `/o` command, which plants each brick in the current selection with its original ownership. Use in plant mode after copying/cutting. `/savedup` now saves ownership, and `/ownership` can be used while holding a loaded duplication to load it. (Loading ownership only works if the duplication was saved with this version, as the original newdup does not save ownership) -Use `/toggleownership` or `/to` to always plant with original ownership, similar to `/toggleforceplant` or `/tfp` -- Added `/prevBox` and `/nextBox` (aliases `/pb` and `/nb`) to recall selection-box history when in box-select mode, similar to pressing up in a chat client or terminal. -- Holding ctrl while making an initial stack selection selects all adjacent bricks of the same color, ignoring diagonals, similar to the fill-can. +Use `/toggleOwnership` or `/to` to always plant with original ownership. +- Added `/prevBox` and `/nextBox` (aliases `/pb` and `/nb`) to recall selection box history when in box-select mode, similar to pressing up in a chat client or terminal. +- Holding ctrl while making an initial stack selection selects all adjacent bricks of the same color, similar to the fill-can, ignoring diagonals. With limited mode off, all adjacent bricks will be selected regardless of color. - The `/alldups` list is nicely formatted, sorted by date, and shows who saved each item. -- Support for Brick_LuaLogic +- Support for Brick_LuaLogic: Supercut can be used on wire bricks. -`/FillBrick LogicWire` or `/fbw` can be used to fill with wire bricks. +`/fillBrick LogicWire` or `/fbw` can be used to fill with wire bricks. Initial-multi-box-selecting a ROM sets the box to its data volume. -- Support for Event_setZoneBox +- Support for Event_setZoneBox: Initial multi-box-selecting a brick with a zone sets the box to its zone. -Hitting plant with this box updates the first Event_SetZoneBox event if present. +Hitting plant with this box updates the first setZoneBox output event if present. ## Tweaks - Made the "Create Sym Table on Start" pref default to true. @@ -26,6 +26,6 @@ Hitting plant with this box updates the first Event_SetZoneBox event if present. ## Fixes - Fixed "Nonexistent undo state" message when undoing a plant that has been supercut. - Fixed preventing unequipping any tool for 1.5s after using `/duplicator` -- Removed useless files from the add-on root directory +- Removed useless files from the add-on root directory. - Removed the annoying messages about mismatched newdup versions when joining a server. - Removed the worm known as Support_Updater. diff --git a/scripts/server/commands.cs b/scripts/server/commands.cs index 449bbd5..0f98a2d 100644 --- a/scripts/server/commands.cs +++ b/scripts/server/commands.cs @@ -46,6 +46,7 @@ function serverCmdDupHelp(%client) messageClient(%client, '', "\c3/ToggleForcePlant\t\c6 Enable force plant for normal planting, so you dont have to type it all the time."); messageClient(%client, '', "\c3/PlantAs\c6 [\c3target\c6]\t\c6 Plant bricks in a different brick group. Target can be a name or blid."); messageClient(%client, '', "\c3/Ownership\t\c6 Plant bricks with the original ownership from when they were copied or saved."); + messageClient(%client, '', "\c3/ToggleOwnership\t\c6 Always plant bricks with original ownership."); messageClient(%client, '', " "); messageClient(%client, '', "\c3/FillWrench\t\c6 Open the fill wrench gui to change settings on all selected bricks."); diff --git a/scripts/server/functions.cs b/scripts/server/functions.cs index ca2a941..84df4ac 100644 --- a/scripts/server/functions.cs +++ b/scripts/server/functions.cs @@ -1,4 +1,3 @@ -//bls 3 // ------------------------------------------------------------------- diff --git a/scripts/server/images.cs b/scripts/server/images.cs index 0429dea..730699b 100644 --- a/scripts/server/images.cs +++ b/scripts/server/images.cs @@ -1,4 +1,4 @@ -//bls 3 + // Handles interactions with the handheld duplicator item. // ------------------------------------------------------------------- diff --git a/scripts/server/logicFillBus.cs b/scripts/server/logicFillBus.cs index 4912385..82a4438 100644 --- a/scripts/server/logicFillBus.cs +++ b/scripts/server/logicFillBus.cs @@ -1,6 +1,4 @@ -//bls 3 -//function ndRotateVector(%vector, %steps) function ndFillBus(%pos1, %pos2, %angleId, %color) { talk(%pos1 @ ", " @ %pos2 @ ", " @ %angleId); $ND::FillBrickColorID = %color + 1; diff --git a/scripts/server/shapeline.cs b/scripts/server/shapeline.cs new file mode 100644 index 0000000..81eb0d1 --- /dev/null +++ b/scripts/server/shapeline.cs @@ -0,0 +1,73 @@ + +function ndShapelineCreate(%color) { + if(%color$="") %color="1 1 1 1"; + + %line = new StaticShape() { + datablock = ND_SelectionBoxBorder; + }; + //%line.setScopeAlways(); // newdup does this, idk why yet + %line.setNodeColor("ALL", %color); + + return %line; +} +function ndShapelineMove(%line, %p1,%p2) { + // calculate shape position + %center = vectorScale(vectorAdd(%p1,%p2), 0.5); + + // calculate shape rotation + %lineDir = "0 0 1"; // direction of static shape + %dir = vectorSub(%p2, %p1); + %axis = vectorNormalize(vectorCross(%dir, %lineDir)); + %angle = mAcos(vectorDot(%dir, %lineDir) / vectorLen(%dir) / vectorLen(%lineDir)); + %rot = %axis SPC %angle; + + // calculate shape scale + %scale = "1 1" SPC %vectorLen(%dir); + + %line.setTransform(%center SPC %rot); + %line.setScale(%scale); +} +function ndShapelineDelete(%line) { + %line.delete(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Box drawing + +function ndShapelineConnectBoxes_delete(%container) { // public + for(%i=0; %i<8; %i++) { + if(isObject(%line = %container.ndShapeline[%i])) + %line.delete(); + } +} +function ndShapelineConnectBoxes_create(%container, %color) { // public + for(%i=0; %i<8; %i++) { + if(!isObject(%container.ndShapeline[%i])) + %container.ndShapeline[%i] = ndShapelineCreate(%color); + } +} + +function ndMin(%a,%b) { return %a<%b ? %a : %b; } +function ndMax(%a,%b) { return %a>%b ? %a : %b; } +// draw 8 lines between the boxes' 8 corners +function ndNormalizeBox(%box) { + %pLx = ndMin(getWord(%box,0),getWord(%box,3)); + %pLy = ndMin(getWord(%box,1),getWord(%box,4)); + %pLz = ndMin(getWord(%box,2),getWord(%box,5)); + %pHx = ndMax(getWord(%box,0),getWord(%box,3)); + %pHy = ndMax(getWord(%box,1),getWord(%box,4)); + %pHz = ndMax(getWord(%box,2),getWord(%box,5)); + return %pLx SPC %pLy SPC %pLz SPC %pHx SPC %pHy SPC %pHz; +} +function ndShapelineConnectBoxes_move(%container, %box1, %box2) { // public + %box1 = ndNormalizeBox(%box1); + %box2 = ndNormalizeBox(%box2); + ndShapelineMove(%container.ndShapeline[0], getWord(%box1,0) SPC getWord(%box1,1) SPC getWord(%box1,2), getWord(%box2,0) SPC getWord(%box2,1) SPC getWord(%box2,2)); + ndShapelineMove(%container.ndShapeline[1], getWord(%box1,0) SPC getWord(%box1,1) SPC getWord(%box1,5), getWord(%box2,0) SPC getWord(%box2,1) SPC getWord(%box2,5)); + ndShapelineMove(%container.ndShapeline[2], getWord(%box1,0) SPC getWord(%box1,4) SPC getWord(%box1,2), getWord(%box2,0) SPC getWord(%box2,4) SPC getWord(%box2,2)); + ndShapelineMove(%container.ndShapeline[3], getWord(%box1,0) SPC getWord(%box1,4) SPC getWord(%box1,5), getWord(%box2,0) SPC getWord(%box2,4) SPC getWord(%box2,5)); + ndShapelineMove(%container.ndShapeline[4], getWord(%box1,3) SPC getWord(%box1,1) SPC getWord(%box1,2), getWord(%box2,3) SPC getWord(%box2,1) SPC getWord(%box2,2)); + ndShapelineMove(%container.ndShapeline[5], getWord(%box1,3) SPC getWord(%box1,1) SPC getWord(%box1,5), getWord(%box2,3) SPC getWord(%box2,1) SPC getWord(%box2,5)); + ndShapelineMove(%container.ndShapeline[6], getWord(%box1,3) SPC getWord(%box1,4) SPC getWord(%box1,2), getWord(%box2,3) SPC getWord(%box2,4) SPC getWord(%box2,2)); + ndShapelineMove(%container.ndShapeline[7], getWord(%box1,3) SPC getWord(%box1,4) SPC getWord(%box1,5), getWord(%box2,3) SPC getWord(%box2,4) SPC getWord(%box2,5)); +} diff --git a/server.cs b/server.cs index 707a8f3..f86adfb 100644 --- a/server.cs +++ b/server.cs @@ -49,6 +49,7 @@ 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/shapeline.cs"); exec($ND::ScriptPath @ "server/symmetrydefinitions.cs"); exec($ND::ScriptPath @ "server/symmetrytable.cs"); exec($ND::ScriptPath @ "server/undo.cs");