initial commit
This commit is contained in:
225
scripts/common/bytetable.cs
Normal file
225
scripts/common/bytetable.cs
Normal 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
1151
scripts/server/commands.cs
Normal file
File diff suppressed because it is too large
Load Diff
344
scripts/server/datablocks.cs
Normal file
344
scripts/server/datablocks.cs
Normal 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
734
scripts/server/functions.cs
Normal 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;
|
||||
}
|
71
scripts/server/handshake.cs
Normal file
71
scripts/server/handshake.cs
Normal 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
106
scripts/server/highlight.cs
Normal 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
297
scripts/server/images.cs
Normal 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
497
scripts/server/modes.cs
Normal 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);
|
||||
}
|
||||
};
|
90
scripts/server/namedtargets.cs
Normal file
90
scripts/server/namedtargets.cs
Normal 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
212
scripts/server/prefs.cs
Normal 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;
|
||||
}
|
180
scripts/server/symmetrydefinitions.cs
Normal file
180
scripts/server/symmetrydefinitions.cs
Normal 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;
|
692
scripts/server/symmetrytable.cs
Normal file
692
scripts/server/symmetrytable.cs
Normal 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
31
scripts/server/undo.cs
Normal 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);
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user