// General server commands used to control the new duplicator. // ------------------------------------------------------------------- //Information commands /////////////////////////////////////////////////////////////////////////// //Shows version of blockland and the new duplicator function serverCmdDupVersion(%client) { messageClient(%client, '', "\c6Blockland version: \c3r" @ getBuildNumber()); messageClient(%client, '', "\c6New duplicator version: \c3" @ $ND::Version); if(%client.ndClient) messageClient(%client, '', "\c6Your new duplicator version: \c3" @ %client.ndVersion); else messageClient(%client, '', "\c6You don't have the new duplicator installed"); } //Shows versions of other clients function serverCmdDupClients(%client) { messageClient(%client, '', "\c6New duplicator versions:"); %cnt = ClientGroup.getCount(); for(%i = 0; %i < %cnt; %i++) { %cl = ClientGroup.getObject(%i); if(%cl.ndClient) messageClient(%client, '', "\c3" @ %cl.name @ "\c6 has \c3" @ %cl.ndVersion); } } //Shows list of commands function serverCmdDupHelp(%client) { messageClient(%client, '', " "); messageClient(%client, '', "\c6You can use the following commands with your new duplicator:"); messageClient(%client, '', "\c7--------------------------------------------------------------------------------"); messageClient(%client, '', "\c3/Duplicator\t\c6 Equip a new duplicator!"); messageClient(%client, '', " "); messageClient(%client, '', "\c3/ForcePlant\t\c6 Plant a selection in mid air; bricks can float."); 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, '', " "); messageClient(%client, '', "\c3/FillWrench\t\c6 Open the fill wrench gui to change settings on all selected bricks."); messageClient(%client, '', " "); messageClient(%client, '', "\c3/MirrorX\t\c6 Mirror your ghost selection left/right on screen."); messageClient(%client, '', "\c3/MirrorY\t\c6 Mirror your ghost selection front/back on screen."); messageClient(%client, '', "\c3/MirrorZ\t\c6 Mirror your ghost selection up/down on screen."); messageClient(%client, '', "\c3/MirErrors\t\c6 List potential mirror errors after planting a mirrored ghost selection."); messageClient(%client, '', " "); messageClient(%client, '', "\c3/SuperCut\t\c6 Delete everything in your selection box, cutting bricks in half on its sides!"); messageClient(%client, '', "\c3/FillBricks\t\c6 First supercut, then completely fill your selection box with few bricks."); messageClient(%client, '', " "); messageClient(%client, '', "\c3/SaveDup\c6 [\c3name\c6]\t\c6 Save your current selection to a file."); messageClient(%client, '', "\c3/LoadDup\c6 [\c3name\c6]\t\c6 Load a selection from a file. Your current selection will be deleted."); messageClient(%client, '', "\c3/AllDups\c6 [\c3filter\c6]\t\c6 Show all known saved duplications that match the filter. Leave blank to show all."); messageClient(%client, '', " "); messageClient(%client, '', "\c3/DupVersion\t\c6 Show the duplicator and blockland versions running on the server."); messageClient(%client, '', "\c3/DupClients\t\c6 Show the duplicator versions of other clients on the server."); messageClient(%client, '', "\c7--------------------------------------------------------------------------------"); messageClient(%client, '', "\c6All of the commands can be shortened by just typing a \c3/\c6 and the capital letters!"); messageClient(%client, '', "\c6You might have to use \c3PageUp\c6/\c3PageDown\c6 to see all of them."); messageClient(%client, '', " "); } //Alternative short commands function serverCmdDV(%client){serverCmdDupVersion(%client);} function serverCmdDC(%client){serverCmdDupClients(%client);} function serverCmdDH(%client){serverCmdDupHelp(%client);} //Equip commands /////////////////////////////////////////////////////////////////////////// //Command to equip the new duplicator function serverCmdNewDuplicator(%client) { //Check admin if($Pref::Server::ND::AdminOnly && !%client.isAdmin) { messageClient(%client, '', "\c6The new duplicator is admin only. Ask an admin for help."); return; } //Check minigame if(isObject(%client.minigame) && !%client.minigame.enablebuilding) { messageClient(%client, '', "\c6You cannot use the new duplicator while building is disabled in your minigame."); return; } //Check player if(!isObject(%player = %client.player)) { messageClient(%client, '', "\c6You must be spawned to equip the new duplicator."); return; } //Hide brick selector and tool gui %client.ndLastEquipTime = $Sim::Time; commandToClient(%client, 'setScrollMode', 3); //Give player a duplicator %image = %client.ndImage; if(!isObject(%image)) %image = ND_Image; %player.updateArm(%image); %player.mountImage(%image, 0); %client.ndEquippedFromItem = false; } //Alternative commands to equip the new duplicator (override old duplicators) package NewDuplicator_Server_Final { function serverCmdDuplicator(%client){serverCmdNewDuplicator(%client);} function serverCmdDuplicato (%client){serverCmdNewDuplicator(%client);} function serverCmdDuplicat (%client){serverCmdNewDuplicator(%client);} function serverCmdDuplica (%client){serverCmdNewDuplicator(%client);} function serverCmdDuplic (%client){serverCmdNewDuplicator(%client);} function serverCmdDupli (%client){serverCmdNewDuplicator(%client);} function serverCmdDupl (%client){serverCmdNewDuplicator(%client);} function serverCmdDup (%client){serverCmdNewDuplicator(%client);} function serverCmdDu (%client){serverCmdNewDuplicator(%client);} function serverCmdD (%client){serverCmdNewDuplicator(%client);} }; //Default keybind commands /////////////////////////////////////////////////////////////////////////// package NewDuplicator_Server { //Light key (default: R) function serverCmdLight(%client) { if(%client.ndModeIndex) %client.ndMode.onLight(%client); else parent::serverCmdLight(%client); } //Next seat (default: .) function serverCmdNextSeat(%client) { if(%client.ndModeIndex) %client.ndMode.onNextSeat(%client); else parent::serverCmdNextSeat(%client); } //Previous seat (default: ,) function serverCmdPrevSeat(%client) { if(%client.ndModeIndex) %client.ndMode.onPrevSeat(%client); else parent::serverCmdPrevSeat(%client); } //Shifting the ghost brick (default: numpad 2468/13/5+) function serverCmdShiftBrick(%client, %x, %y, %z) { if(%client.ndModeIndex) %client.ndMode.onShiftBrick(%client, %x, %y, %z); //Call parent to play animation parent::serverCmdShiftBrick(%client, %x, %y, %z); } //Super-shifting the ghost brick (default: alt numpad 2468/5+) function serverCmdSuperShiftBrick(%client, %x, %y, %z) { if(%client.ndModeIndex) %client.ndMode.onSuperShiftBrick(%client, %x, %y, %z); //Call parent to play animation parent::serverCmdSuperShiftBrick(%client, %x, %y, %z); } //Rotating the ghost brick (default: numpad 79) function serverCmdRotateBrick(%client, %direction) { if(%client.ndModeIndex) %client.ndMode.onRotateBrick(%client, %direction); //Call parent to play animation parent::serverCmdRotateBrick(%client, %direction); } //Undo bricks (default: ctrl z) function serverCmdUndoBrick(%client) { if(%client.ndUndoInProgress) { messageClient(%client, '', "\c6Please wait for the current undo task to finish."); return; } //This really needs a better api. //Wtf were you thinking, badspot? %state = %client.undoStack.pop(); %type = getField(%state, 1); if( %type $= "ND_PLANT" || %type $= "ND_PAINT" || %type $= "ND_WRENCH" ){ %obj = getField(%state, 0); if(isObject(%obj)){ if(%obj.brickCount > 10 && %client.ndUndoConfirm != %obj) { messageClient(%client, '', "\c6Next undo will affect \c3" @ %obj.brickCount @ "\c6 bricks. Press undo again to continue."); %client.undoStack.push(%state); %client.ndUndoConfirm = %obj; return; } %obj.ndStartUndo(%client); if(isObject(%client.player)) %client.player.playThread(3, "undo"); %client.ndUndoConfirm = 0; }else{ talk("serverCmdUndoBrick(" @ %client.name @ ") - Nonexistent undo state " @ %state); } return; } %client.ndUndoConfirm = 0; %client.undoStack.push(%state); parent::serverCmdUndoBrick(%client); } }; package NewDuplicator_Server_Final { //Planting the ghost brick (default: numpad enter) function serverCmdPlantBrick(%client) { if(%client.ndModeIndex) %client.ndMode.onPlantBrick(%client); else parent::serverCmdPlantBrick(%client); } //Removing the ghost brick (default: numpad 0) function serverCmdCancelBrick(%client) { if(%client.ndModeIndex) %client.ndMode.onCancelBrick(%client); else parent::serverCmdCancelBrick(%client); } }; //Custom keybind commands /////////////////////////////////////////////////////////////////////////// //Copy selection (ctrl c) function serverCmdNdCopy(%client) { if(%client.ndModeIndex) %client.ndMode.onCopy(%client); } //Paste selection (ctrl v) function serverCmdNdPaste(%client) { if(%client.ndModeIndex) %client.ndMode.onPaste(%client); } //Cut selection (ctrl x) function serverCmdNdCut(%client) { if(%client.ndModeIndex) %client.ndMode.onCut(%client); } //Cut selection function serverCmdCut(%client) { serverCmdNdCut(%client); } //Supercut selection function serverCmdSuperCut(%client) { if(%client.ndModeIndex != $NDM::BoxSelect) { messageClient(%client, '', "\c6Supercut can only be used on box selection mode."); return; } if(!isObject(%client.ndSelectionBox)) { messageClient(%client, '', "\c6Supercut can only be used with a selection box."); return; } if(%client.ndSelectionAvailable) { messageClient(%client, '', "\c6Supercut can not be used with any bricks selected."); return; } commandToClient(%client, 'messageBoxOkCancel', "New Duplicator | Supercut", "Supercut is destructive and does\nNOT support undo at this time." @ "\n\nPlease make sure the box is correct,\nthen press OK below.", 'ndConfirmSuperCut'); } //Confirm Supercut selection function serverCmdNdConfirmSuperCut(%client) { if(%client.ndModeIndex != $NDM::BoxSelect) { messageClient(%client, '', "\c6Supercut can only be used on box selection mode."); return; } if(!isObject(%client.ndSelectionBox)) { messageClient(%client, '', "\c6Supercut can only be used with a selection box."); return; } if(%client.ndSelectionAvailable) { messageClient(%client, '', "\c6Supercut can not be used with any bricks selected."); return; } %client.NDFillBrickSubset = $ND::SubsetDefault; %client.fillBricksAfterSuperCut = false; %client.ndMode.onSuperCut(%client); } //Alternative short command function serverCmdSC(%client){serverCmdSuperCut(%client);} //Fill volume with bricks function serverCmdFillBricks(%client, %subsetname) { if($Pref::Server::ND::FillBricksAdminOnly && !%client.isAdmin) { messageClient(%client, '', "\c6Fill Bricks is admin only. Ask an admin for help."); return; } if(!isObject(%client.ndSelectionBox)) { messageClient(%client, '', "\c6The fillBricks command can only be used with a selection box."); return; } if(%client.ndSelectionAvailable) { messageClient(%client, '', "\c6The fillBricks command can not be used with any bricks selected."); return; } if(!%client.ndSelectionBox.hasVolume()) { messageClient(%client, '', "\c6The fillBricks command can only be used with a selection box that has a volume."); return; } %client.NDFillBrickSubset = (%subsetname !$= "") ? ndLookupSubsetName(%subsetname) : $ND::SubsetDefault; commandToClient(%client, 'messageBoxOkCancel', "New Duplicator | /FillBricks", "/FillBricks will first do a Supercut\nbefore placing bricks, to fix overlap." @ "\n\nSupercut is destructive and does\nNOT support undo at this time." @ "\n\nPlease make sure the box is correct,\nthen press OK below to continue.", 'ndConfirmFillBricks'); } //Confirm fill volume with bricks function serverCmdNdConfirmFillBricks(%client, %subsetname) { if($Pref::Server::ND::FillBricksAdminOnly && !%client.isAdmin) { messageClient(%client, '', "\c6Fill Bricks is admin only. Ask an admin for help."); return; } if(!isObject(%client.ndSelectionBox)) { messageClient(%client, '', "\c6The fillBricks command can only be used with a selection box."); return; } if(%client.ndSelectionAvailable) { messageClient(%client, '', "\c6The fillBricks command can not be used with any bricks selected."); return; } if(!%client.ndSelectionBox.hasVolume()) { messageClient(%client, '', "\c6The fillBricks command can only be used with a selection box that has a volume."); return; } %client.NDFillBrickSubset = (%subsetname !$= "") ? ndLookupSubsetName(%subsetname) : ((%client.NDFillBrickSubset !$= "") ? %client.NDFillBrickSubset : $ND::SubsetDefault ) ; %client.fillBricksAfterSuperCut = true; %client.ndMode.onSuperCut(%client); } //Alternative short command function serverCmdFB(%client){serverCmdFillBricks(%client);} function serverCmdFBW(%client) { serverCmdFillBricks(%client, "LogicWire"); } //MultiSelect toggle (ctrl) function serverCmdNdMultiSelect(%client, %bool) { %client.ndMultiSelect = !!%bool; if(%client.ndModeIndex == $NDM::StackSelect || %client.ndModeIndex == $NDM::BoxSelect) %client.ndUpdateBottomPrint(); } //Mirror commands /////////////////////////////////////////////////////////////////////////// //Mirror selection on X relative to player function serverCmdMirrorX(%client) { if((getAngleIDFromPlayer(%client.getControlObject()) - %client.ndSelection.ghostAngleID) % 2 == 1) %client.ndMirror(0); else %client.ndMirror(1); } //Mirror selection on Y relative to player function serverCmdMirrorY(%client) { if((getAngleIDFromPlayer(%client.getControlObject()) - %client.ndSelection.ghostAngleID) % 2 == 1) %client.ndMirror(1); else %client.ndMirror(0); } //Mirror selection on Z function serverCmdMirrorZ(%client) { %client.ndMirror(2); } //Alternative short commands function serverCmdMX(%client){serverCmdMirrorX(%client);} function serverCmdMY(%client){serverCmdMirrorY(%client);} function serverCmdMZ(%client){serverCmdMirrorZ(%client);} //Attempt to mirror selection on axis function GameConnection::ndMirror(%client, %axis) { //Make sure symmetry table is created if(!$ND::SymmetryTableCreated) { if(!$ND::SymmetryTableCreating) ndCreateSymmetryTable(); messageClient(%client, '', "\c6Please wait for the symmetry table to finish, then mirror again."); return; } //If we're in plant mode, mirror the selection if(isObject(%client.ndSelection) && %client.ndModeIndex == $NDM::PlantCopy) { %client.ndSelection.mirrorGhostBricks(%axis); return; } //If we have a ghost brick, mirror that instead if(isObject(%client.player) && isObject(%client.player.tempBrick)) { %client.player.tempBrick.ndMirrorGhost(%client, %axis); return; } //We didn't mirror anything messageClient(%client, '', "\c6The mirror command can only be used in plant mode or with a ghost brick."); } //List potential mirror errors in last plant function serverCmdMirErrors(%client) { %xerr = $NS[%client, "MXC"]; %zerr = $NS[%client, "MZC"]; if(%xerr) { messageClient(%client, '', " "); messageClient(%client, '', "\c6These bricks are asymmetric and probably mirrored incorrectly:"); for(%i = 0; %i < %xerr; %i++) { %db = $NS[%client, "MXE", %i]; messageClient(%client, '', "\c7 -" @ %i + 1 @ "- \c6" @ %db.category @ "/" @ %db.subCategory @ "/" @ %db.uiName); } } if(%zerr) { messageClient(%client, '', " "); messageClient(%client, '', "\c6These bricks are not vertically symmetric and probably incorrect:"); for(%i = 0; %i < %zerr; %i++) { %db = $NS[%client, "MZE", %i]; messageClient(%client, '', "\c7 -" @ %i + 1 @ "- \c6" @ %db.category @ "/" @ %db.subCategory @ "/" @ %db.uiName); } } if(!%xerr && !%zerr) messageClient(%client, '', "\c6There were no mirror errors in your last plant attempt."); } //Alternative short command function serverCmdME(%client){serverCmdMirErrors(%client);} //Force plant /////////////////////////////////////////////////////////////////////////// //Force plant one time function serverCmdForcePlant(%client) { //Check mode if(%client.ndModeIndex != $NDM::PlantCopy) { messageClient(%client, '', "\c6Force Plant can only be used in Plant Mode."); return; } //Check admin if($Pref::Server::ND::FloatAdminOnly && !%client.isAdmin) { messageClient(%client, '', "\c6Force Plant is admin only. Ask an admin for help."); return; } NDM_PlantCopy.conditionalPlant(%client, true); } //Alternative short command function serverCmdFP(%client){serverCmdForcePlant(%client);} //Keep force plant enabled function serverCmdToggleForcePlant(%client) { //Check admin if($Pref::Server::ND::FloatAdminOnly && !%client.isAdmin) { messageClient(%client, '', "\c6Force Plant is admin only. Ask an admin for help."); return; } %client.ndForcePlant = !%client.ndForcePlant; if(%client.ndForcePlant) messageClient(%client, '', "\c6Force Plant has been enabled. Use \c3/toggleForcePlant\c6 to disable it."); else messageClient(%client, '', "\c6Force Plant has been disabled. Use \c3/toggleForcePlant\c6 to enable it again."); } //Alternative short command function serverCmdTFP(%client){serverCmdToggleForcePlant(%client);} //Fill color /////////////////////////////////////////////////////////////////////////// package NewDuplicator_Server { //Enable fill color mode or show the current color function serverCmdUseSprayCan(%client, %index) { %mode = %client.ndModeIndex; if(%mode == $NDM::StackSelect || %mode == $NDM::BoxSelect) { if(isObject(%client.ndSelection) && %client.ndSelection.brickCount) { %client.currentColor = %index; %client.currentFxColor = ""; %client.ndSetMode(NDM_FillColor); return; } } else if(%mode == $NDM::FillColor || %client.ndModeIndex == $NDM::FillColorProgress) { %client.currentColor = %index; %client.currentFxColor = ""; %client.ndUpdateBottomPrint(); return; } cancel(%client.ndToolSchedule); parent::serverCmdUseSprayCan(%client, %index); } //Enable fill color mode or show the current color function serverCmdUseFxCan(%client, %index) { %mode = %client.ndModeIndex; if(%mode == $NDM::StackSelect || %mode == $NDM::BoxSelect) { if(isObject(%client.ndSelection) && %client.ndSelection.brickCount) { %client.currentFxColor = %index; %client.ndSetMode(NDM_FillColor); } else parent::serverCmdUseFxCan(%client, %index); } else if(%mode == $NDM::FillColor || %client.ndModeIndex == $NDM::FillColorProgress) { %client.currentFxColor = %index; %client.ndUpdateBottomPrint(); } else parent::serverCmdUseFxCan(%client, %index); } }; //Fill wrench /////////////////////////////////////////////////////////////////////////// //Open the fill wrench gui function serverCmdFillWrench(%client) { //Check version if(!%client.ndClient) { messageClient(%client, '', "\c6You need to have the new duplicator installed to use Fill Wrench."); return; } if(ndCompareVersion("1.2.0", %client.ndVersion) == 1) { messageClient(%client, '', "\c6Your version of the new duplicator is too old to use Fill Wrench."); return; } //Check admin if($Pref::Server::ND::WrenchAdminOnly && !%client.isAdmin) { messageClient(%client, '', "\c6Fill Wrench is admin only. Ask an admin for help."); return; } //Check mode if(%client.ndModeIndex != $NDM::StackSelect && %client.ndModeIndex != $NDM::BoxSelect) { messageClient(%client, '', "\c6Fill Wrench can only be used in Selection Mode."); return; } //Check selection if(!isObject(%client.ndSelection) || !%client.ndSelection.brickCount) { messageClient(%client, '', "\c6Fill Wrench can only be used with a selection."); return; } //Open fill wrench gui commandToClient(%client, 'ndOpenWrenchGui'); } //Short command function serverCmdFW(%client) {serverCmdFillWrench(%client);} //Send data from gui function serverCmdNdStartFillWrench(%client, %data) { //Check admin if($Pref::Server::ND::WrenchAdminOnly && !%client.isAdmin) { messageClient(%client, '', "\c6Fill Wrench is admin only. Ask an admin for help."); return; } //Check mode if(%client.ndModeIndex != $NDM::StackSelect && %client.ndModeIndex != $NDM::BoxSelect) { messageClient(%client, '', "\c6Fill Wrench can only be used in Selection Mode."); return; } //Check selection if(!isObject(%client.ndSelection) || !%client.ndSelection.brickCount) { messageClient(%client, '', "\c6Fill Wrench can only be used with a selection."); return; } //Change mode %client.ndSetMode(NDM_WrenchProgress); %client.ndSelection.startFillWrench(%data); } //Saving and loading /////////////////////////////////////////////////////////////////////////// package NewDuplicator_Server_Final { //Save current selection to file function serverCmdSaveDup(%client, %f0, %f1, %f2, %f3, %f4, %f5, %f6, %f7) { //Check timeout if(!%client.isAdmin && %client.ndLastSaveTime + 10 > $Sim::Time) { %remain = mCeil(%client.ndLastSaveTime + 10 - $Sim::Time); if(%remain != 1) %s = "s"; messageClient(%client, '', "\c6Please wait\c3 " @ %remain @ "\c6 second" @ %s @ " before saving again!"); return; } //Check admin if($Pref::Server::ND::SaveAdminOnly && !%client.isAdmin) { messageClient(%client, '', "\c6Saving duplications is admin only. Ask an admin for help."); return; } //Check mode if(%client.ndModeIndex != $NDM::PlantCopy) { messageClient(%client, '', "\c6Saving duplications can only be used in Plant Mode."); return; } //Filter file name %allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ._-()"; %fileName = trim(%f0 SPC %f1 SPC %f2 SPC %f3 SPC %f4 SPC %f5 SPC %f6 SPC %f7); %filePath = $ND::ConfigPath @ "Saves/" @ %fileName @ ".bls"; %filePath = strReplace(%filePath, ".bls.bls", ".bls"); for(%i = 0; %i < strLen(%fileName); %i++) { if(strStr(%allowed, getSubStr(%fileName, %i, 1)) == -1) { %forbidden = true; break; } } if(%forbidden || !strLen(%fileName) || strLen(%fileName) > 50) { messageClient(%client, '', "\c6Bad save name \"\c3" @ %fileName @ "\c6\", please try again."); messageClient(%client, '', "\c6Only \c3a-z A-Z 0-9 ._-()\c6 and \c3space\c6 are allowed, with a max length of 50 characters."); return; } //Check overwrite if(isFile(%filePath) && %client.ndPotentialOverwrite !$= %fileName) { messageClient(%client, '', "\c6Save \"\c3" @ %fileName @ "\c6\" already exists. Repeat the command to overwrite."); %client.ndPotentialOverwrite = %fileName; return; } %client.ndPotentialOverwrite = ""; //Check writeable if(!isWriteableFileName(%filePath)) { messageClient(%client, '', "\c6File \"\c3" @ %fileName @ "\c6\" is not writeable. Ask the host for help."); return; } messageClient(%client, '', "\c6Saving selection to \"\c3" @ %fileName @ "\c6\"..."); //Notify admins if(!%client.isAdmin) { for(%i = 0; %i < ClientGroup.getCount(); %i++) { %cl = ClientGroup.getObject(%i); if(%cl.isAdmin && %cl != %client) messageClient(%cl, '', "\c3" @ %client.name @ "\c6 is saving duplication \"\c3" @ %fileName @ "\c6\""); } } //Write log echo("ND: " @ %client.name @ " (" @ %client.bl_id @ ") is saving duplication \"" @ %fileName @ "\""); // Uncache saved file info $ND::FileDate[%filePath] = ""; //Change mode %client.ndSetMode(NDM_SaveProgress); if(!%client.ndSelection.startSaving(%filePath)) { messageClient(%client, '', "\c6Failed to write save \"\c3" @ %fileName @ "\c6\". Ask the host for help."); %client.ndSetMode(NDM_PlantCopy); } } //Load selection from file function serverCmdLoadDup(%client, %f0, %f1, %f2, %f3, %f4, %f5, %f6, %f7) { //Check timeout if(!%client.isAdmin && %client.ndLastLoadTime + 5 > $Sim::Time) { %remain = mCeil(%client.ndLastLoadTime + 5 - $Sim::Time); if(%remain != 1) %s = "s"; messageClient(%client, '', "\c6Please wait\c3 " @ %remain @ "\c6 second" @ %s @ " before loading again!"); return; } //Check admin if($Pref::Server::ND::LoadAdminOnly && !%client.isAdmin) { messageClient(%client, '', "\c6Loading duplications is admin only. Ask an admin for help."); return; } //Attempt to get a duplicator if(!%client.ndEquipped) { serverCmdNewDuplicator(%client); if(!%client.ndEquipped) return; } //Check mode %mode = %client.ndModeIndex; if(%mode != $NDM::StackSelect && %mode != $NDM::BoxSelect && %mode != $NDM::PlantCopy) { messageClient(%client, '', "\c6Loading duplications can only be used in Plant or Selection Mode."); return; } //Filter file name %allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ._-()"; %fileName = trim(%f0 SPC %f1 SPC %f2 SPC %f3 SPC %f4 SPC %f5 SPC %f6 SPC %f7); %filePath = $ND::ConfigPath @ "Saves/" @ %fileName @ ".bls"; %filePath = strReplace(%filePath, ".bls.bls", ".bls"); for(%i = 0; %i < strLen(%fileName); %i++) { if(strStr(%allowed, getSubStr(%fileName, %i, 1)) == -1) { %forbidden = true; break; } } if(%forbidden || !strLen(%fileName) || strLen(%fileName) > 50) { messageClient(%client, '', "\c6Bad save name \"\c3" @ %fileName @ "\c6\", please try again."); messageClient(%client, '', "\c6Only \c3a-z A-Z 0-9 ._-()\c6 and \c3space\c6 are allowed, with a max length of 50 characters."); return; } //Check if file exists if(!isFile(%filePath)) { messageClient(%client, '', "\c6Save \"\c3" @ %fileName @ "\c6\" does not exist, please try again."); return; } messageClient(%client, '', "\c6Loading selection from \"\c3" @ %fileName @ "\c6\"..."); //Notify admins if(!%client.isAdmin) { for(%i = 0; %i < ClientGroup.getCount(); %i++) { %cl = ClientGroup.getObject(%i); if(%cl.isAdmin && %cl != %client) messageClient(%cl, '', "\c3" @ %client.name @ "\c6 is loading duplication \"\c3" @ %fileName @ "\c6\""); } } //Write log echo("ND: " @ %client.name @ " (" @ %client.bl_id @ ") is loading duplication \"" @ %fileName @ "\""); //Change mode %client.ndSetMode(NDM_LoadProgress); if(!%client.ndSelection.startLoading(%filePath)) { messageClient(%client, '', "\c6Failed to read save \"\c3" @ %fileName @ "\c6\". Ask the host for help."); %client.ndSetMode(%client.ndLastSelectMode); } } }; function ND_SaveFileInfo(%filename) { // Get date from file if not cached if($ND::FileDate[%filename] $= "") { %file = new FileObject(); %file.openForRead(%filename); %file.readLine(); %file.readLine(); %dateline = %file.readLine(); %file.close(); %file.delete(); $ND::FileDate[%filename] = %dateline; } // Construct table line %info = $ND::FileDate[%filename]; %info_aftername = getSubStr(%info, strStr(%info, " (")+2, strLen(%info)); %date = getSubStr(%info_aftername, strLen(%info_aftername)-17, 17); %blid = getSubStr(%info_aftername, 0, strStr(%info_aftername, ")")); %name = getSubStr(%info, 9, strStr(%info, " (") - 9); // Fix date format for sorting %sort = strReplace(strReplace(%date, "/", " "), ":", " "); %sort = getWord(%sort, 2) SPC getWord(%sort, 0) SPC getWord(%sort, 1) SPC getWords(%sort, 3, 5) @ " "; %namepart = fileBase(%filename); %filetext = %sort @ "\c3" @ %namepart TAB "\c3" @ %name TAB "\c6" @ %blid TAB "\c6" @ %date ; return %filetext; } //Get list of all available dups function serverCmdAllDups(%client, %pattern) { //Check admin if($Pref::Server::ND::LoadAdminOnly && !%client.isAdmin) { messageClient(%client, '', "\c6Loading duplications is admin only. Ask an admin for help."); return; } if(strLen(%pattern)) { //Filter pattern %allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-()"; %pattern = trim(%pattern); for(%i = 0; %i < strLen(%pattern); %i++) { if(strStr(%allowed, getSubStr(%pattern, %i, 1)) == -1) { messageClient(%client, '', "\c6Bad pattern \"\c3" @ %pattern @ "\c6\", please try again."); messageClient(%client, '', "\c6Only \c3a-z A-Z 0-9 ._-()\c6 are allowed."); return; } } %p = $ND::ConfigPath @ "Saves/*" @ %pattern @ "*.bls"; } else { %p = $ND::ConfigPath @ "Saves/*.bls"; } //Get sorted list of files %sort = new GuiTextListCtrl(); for(%filename = findFirstFile(%p); isFile(%filename); %filename = findNextFile(%p)) { %info = ND_SaveFileInfo(%filename); %sort.addRow(0, %info); } %fileCount = %sort.rowCount(); %sort.sort(0, true); //Dump list to client if(%fileCount) { %s = (%fileCount == 1) ? " is" : "s are"; if(strLen(%pattern)) messageClient(%client, '', "\c3" @ %fileCount @ "\c6 saved duplication" @ %s @ " available for filter \"\c3" @ %pattern @ "\c6\":"); else messageClient(%client, '', "\c3" @ %fileCount @ "\c6 saved duplication" @ %s @ " available:"); %format = ""; if(%fileCount>0) { messageClient(%client, '', %format @ "\c6Name\t\c6Saved By\t\c6BL_ID\t\c6Date"); } for(%i = 0; %i < %fileCount; %i++) { %text = %sort.getRowText(%i); messageClient(%client, '', %format @ getSubStr(%text, 20, strLen(%text))); } } else { if(strLen(%pattern)) messageClient(%client, '', "\c6No saved duplications are available for filter \"\c3" @ %pattern @ "\c6\"."); else messageClient(%client, '', "\c6No saved duplications are available."); } %sort.delete(); messageClient(%client, '', "\c6Scroll using \c3PageUp\c6 and \c3PageDown\c6 if you can't see the whole list."); } //Alternative short commands function serverCmdSD(%client, %f0, %f1, %f2, %f3, %f4, %f5, %f6, %f7) {serverCmdSaveDup(%client, %f0, %f1, %f2, %f3, %f4, %f5, %f6, %f7);} function serverCmdLD(%client, %f0, %f1, %f2, %f3, %f4, %f5, %f6, %f7) {serverCmdLoadDup(%client, %f0, %f1, %f2, %f3, %f4, %f5, %f6, %f7);} function serverCmdAD(%client, %pattern) {serverCmdAllDups(%client, %pattern);} //Admin commands /////////////////////////////////////////////////////////////////////////// //Cancel all active dups in case of spamming function serverCmdClearDups(%client) { if(!%client.isAdmin) { messageClient(%client, '', "\c6Canceling all duplicators is admin only. Ask an admin for help."); return; } messageAll('MsgClearBricks', "\c3" @ %client.getPlayerName() @ "\c0 canceled all duplicators."); for(%i = 0; %i < ClientGroup.getCount(); %i++) { %cl = ClientGroup.getObject(%i); if(%cl.ndModeIndex) %cl.ndKillMode(); } } //Plant as a different brick group function serverCmdPlantAs(%client, %t0, %t1, %t2, %t3, %t4) { //Check mode if(%client.ndModeIndex != $NDM::PlantCopy) { messageClient(%client, '', "\c6This command can only be used in Plant Mode."); return; } //Empty target to clear if(!strLen(%t0)) { messageClient(%client, '', "\c6Bricks will be planted in your own group!"); %client.ndSelection.targetGroup = ""; %client.ndSelection.targetBlid = ""; %client.ndUpdateBottomPrint(); return; } for(%i = 0; strLen(%t[%i]); %i++) %target = %target SPC %t[%i]; //Attempt to find the brick group by name or blid %target = trim(%target); %targetClient = findClientByName(%target); if(!isObject(%targetClient)) %targetClient = findClientByBL_ID(%target); if(isObject(%targetClient)) %targetGroup = %targetClient.brickGroup; else if((%target | 0) $= %target) %targetGroup = nameToId("BrickGroup_" @ %target); if(!isObject(%targetGroup)) { messageClient(%client, '', "\c6No brick group was found for \"\c3" @ %target @ "\c6\"."); messageClient(%client, '', "\c6Bricks will be planted in your own group!"); %client.ndSelection.targetGroup = ""; %client.ndSelection.targetBlid = ""; %client.ndUpdateBottomPrint(); return; } //Check whether we have trust to the target group if(getTrustLevel(%client, %targetGroup) < 1 && (!%client.isAdmin || !$Pref::Server::ND::AdminTrustBypass2)) { messageClient(%client, '', "\c6You need build trust with \c3" @ %targetGroup.name @ "\c6 to plant bricks in their group."); %client.ndSelection.targetGroup = ""; %client.ndSelection.targetBlid = ""; %client.ndUpdateBottomPrint(); return; } //Should be good to go %name = %targetGroup.name; if(getSubStr(%name, strLen(%name) - 1, 1) $= "s") messageClient(%client, '', "\c6Bricks will now be planted in \c3" @ %name @ "\c6' group!"); else messageClient(%client, '', "\c6Bricks will now be planted in \c3" @ %name @ "\c6's group!"); %client.ndSelection.targetGroup = %targetGroup; %client.ndSelection.targetBlid = %targetGroup.bl_id; %client.ndUpdateBottomPrint(); } //Alternative short command function serverCmdPA(%client, %t0, %t1, %t2, %t3, %t4) {serverCmdPlantAs(%client, %t0, %t1, %t2, %t3, %t4);}