initial commit
This commit is contained in:
		
							
								
								
									
										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
	 Redo
					Redo