/// <summary> /// Inverts the given color /// </summary> /// <param name=""></param> /// <returns></returns> public static Color Invert(this Color col) { col.R = (byte)FastMath.FastClamp(255 - col.R, 0, 255); col.G = (byte)FastMath.FastClamp(255 - col.G, 0, 255); col.B = (byte)FastMath.FastClamp(255 - col.B, 0, 255); return(col); }
public Player(ulong id) : base(id, Vector3.Zero) { base.eyePosition = new Vector3(0, 1.45f, 0); _coll = BoundingBoxExtensions.FromSize(.6f, 1.8f, 0.6f); inventory = new Inventory(9 * 4 + 4 + 1); //Inventory storage + armor + offhand //init inventory for (int i = 0; i < inventory.items.Length; i++) { inventory.items[i].ItemCount = 1; inventory.items[i].ItemID = (ushort)FastMath.FastClamp(i + 1, 0, 7); } }
public void Rotate(float xAxis, float yAxis) { if (!float.IsNaN(xAxis)) { Yaw += xAxis; } if (!float.IsNaN(Yaw)) { Pitch = FastMath.FastClamp(Pitch + yAxis, -1.547501f, 1.532499f); } //Check angles /*float x = rotation.X % 360; * float y = rotation.Y % 360; * float z = rotation.Z % 360;*/ }
public override void Draw() { if (base.screenVisible) { _size = UiStateManager.MeasureString(text); UiTexture img = TextureManager.GetUiUv("button_disabled"); UiStateManager.Draw9SplicedButton(img, this); //Draw the button if (enabled) { img = TextureManager.GetUiUv("button_neutral"); if (isHovered && Parent.FocusedComponent != null && Parent.FocusedComponent == this) { if (isClicked) { img = TextureManager.GetUiUv("button_clicked"); } else { img = TextureManager.GetUiUv("button_hover"); } } if (isSliding) { UiStateManager.Draw9SplicedButton(img, new Point(HandleWidth * GameSettings.UIScale, size.Y), new Point(FastMath.FastClamp(InputManager.MouseX + MouseOffset.X, location.X, location.X + size.X - HandleWidth * GameSettings.UIScale), location.Y)); } else { //location.X = component.location.X + % of slider value * component.size.X handleX = FastMath.FastClamp(location.X + (_value * size.X / (_maxValue - _minValue)), location.X, location.X + size.X - HandleWidth * GameSettings.UIScale); UiStateManager.Draw9SplicedButton(img, new Point(HandleWidth * GameSettings.UIScale, size.Y), new Point(handleX, location.Y)); } UiStateManager.DrawText(location.X + size.X / 2 - _size.X / 2, location.Y + size.Y / 2 - _size.Y / 2, buttonColor, text); } else { UiStateManager.Draw9SplicedButton(img, new Point(HandleWidth * GameSettings.UIScale, size.Y), new Point(FastMath.FastClamp(location.X + (_value * size.X / (_maxValue - _minValue)), location.X, location.X + size.X - HandleWidth * GameSettings.UIScale), location.Y)); UiStateManager.DrawText(location.X + size.X / 2 - _size.X / 2, location.Y + size.Y / 2 - _size.Y / 2, buttonDisabledColor, text); } } }
public void DrawText(string text, Color color, int leftMostCharacter, int caretPosition, bool CaretVisible) { string finalString = text.Substring(leftMostCharacter); DrawStringClip(size.X - 2 * paddingX + location.X, UiStateManager.gameFont, finalString, new Vector2(location.X + paddingX * GameSettings.UIScale, location.Y + paddingY * GameSettings.UIScale), color, 0.0f, Vector2.Zero, new Vector2(GameSettings.UITextScale), SpriteEffects.None, 0); if (CaretVisible) { Vector2 textWidth = UiStateManager.gameFont.MeasureString("L"); UiStateManager.DrawImage(new Rectangle( location.X + paddingX * GameSettings.UIScale + (int)UiStateManager.gameFont.MeasureString(text.Substring(leftMostCharacter, caretPosition - leftMostCharacter)).X, //Width location.Y + paddingY, 2 * GameSettings.UIScale, FastMath.FastClamp((int)(textWidth.Y * GameSettings.UIScale), 0, size.Y - 2 * paddingY)), TextureLoader.getWhitePixel(), placeholderColor); } }
public void GetCaretUnderMouse() { //Calculate the character index of where we clicked int relativeX = InputManager.MouseX - location.X; //mouse relative to the textbox //now that we have the position of the cursor, calculate the index of the caret //get the string on screen string renderStr = text.Substring(scrollPosition); int selectedIndex = scrollPosition; for (int i = 0; i < renderStr.Length; i++) { var txVisible = renderStr.Substring(0, i + 1); var currentChar = renderStr.Substring(i, FastMath.FastClamp(i + 1, 0, 1)); var txtSize = UiStateManager.MeasureString(txVisible); var charWidth = UiStateManager.MeasureString(currentChar); if (txtSize.X + charWidth.X < relativeX) { selectedIndex++; } if (txtSize.X + charWidth.X >= relativeX) { this.caretPosition = selectedIndex; break; } if (i == renderStr.Length - 1) { this.caretPosition = renderStr.Length; break; } } }
public void Update() { try { long ProcessingTime = 0L; while (ThreadManager.RunChunkManagerThread) { ProcessingTime = (long)DateTime.UtcNow.TimeOfDay.TotalMilliseconds; Profiler.Start("Chunk Manager Update", Color.MediumPurple); #region Legacy Infinite Terrain Code //Update the tick counters //Handle loading and unloading //First unload all bad chunks //Bad being they are too far away //Also keep them in a temporary Vector3Int array representing chunk positions of loaded chunks #region Block Updates //Sync block updates lock (updates) { lock (LoadedChunkPositions) { Logger.info(-1, "Update Count: " + updates.Count); updatedBlocks = !(updates.Count == 0); for (int i = 0; i < updates.Count; i++) { int targetX = int.MinValue, targetY = int.MinValue, targetZ = int.MinValue; #region Chunk Hedging if (updates[i].X == 0) { targetX = updates[i].ChunkPos.X - 1; targetY = targetY == int.MinValue ? updates[i].ChunkPos.Y : targetY; targetZ = targetZ == int.MinValue ? updates[i].ChunkPos.Z : targetZ; } if (updates[i].X == Chunk.CHUNK_SIZE - 1) { targetX = updates[i].ChunkPos.X + 1; targetY = targetY == int.MinValue ? updates[i].ChunkPos.Y : targetY; targetZ = targetZ == int.MinValue ? updates[i].ChunkPos.Z : targetZ; } if (updates[i].Y == 0) { targetX = targetX == int.MinValue ? updates[i].ChunkPos.X : targetX; targetY = updates[i].ChunkPos.Y - 1; targetZ = targetZ == int.MinValue ? updates[i].ChunkPos.Z : targetZ; } if (updates[i].Y == Chunk.CHUNK_SIZE - 1) { targetX = targetX == int.MinValue ? updates[i].ChunkPos.X : targetX; targetY = updates[i].ChunkPos.Y + 1; targetZ = targetZ == int.MinValue ? updates[i].ChunkPos.Z : targetZ; } if (updates[i].Z == 0) { targetX = targetX == int.MinValue ? updates[i].ChunkPos.X : targetX; targetY = targetY == int.MinValue ? updates[i].ChunkPos.Y : targetY; targetZ = updates[i].ChunkPos.Z - 1; } if (updates[i].Z == Chunk.CHUNK_SIZE - 1) { targetX = targetX == int.MinValue ? updates[i].ChunkPos.X : targetX; targetY = targetY == int.MinValue ? updates[i].ChunkPos.Y : targetY; targetZ = updates[i].ChunkPos.Z + 1; } #endregion Vector3Int targetPosX = new Vector3Int(targetX, updates[i].ChunkPos.Y, updates[i].ChunkPos.Z); Vector3Int targetPosY = new Vector3Int(updates[i].ChunkPos.X, targetY, updates[i].ChunkPos.Z); Vector3Int targetPosZ = new Vector3Int(updates[i].ChunkPos.X, updates[i].ChunkPos.Y, targetZ); if (LoadedChunkPositions.Contains(updates[i].ChunkPos)) { if (chunks[ChunkIndexMapping[updates[i].ChunkPos]].isLoaded && chunks.Count <= ChunkIndexMapping[updates[i].ChunkPos]) { chunks[ChunkIndexMapping[updates[i].ChunkPos]].SetBlock(updates[i].BlockID, updates[i].X, updates[i].Y, updates[i].Z, updates[i].Reason); } } if (LoadedChunkPositions.Contains(targetPosX)) { if (chunks[ChunkIndexMapping[targetPosX]].isLoaded && chunks.Count <= ChunkIndexMapping[targetPosX]) { chunks[ChunkIndexMapping[targetPosX]].UpdateNeeded = true; } } if (LoadedChunkPositions.Contains(targetPosY)) { if (chunks[ChunkIndexMapping[targetPosY]].isLoaded && chunks.Count <= ChunkIndexMapping[targetPosY]) { chunks[ChunkIndexMapping[targetPosY]].UpdateNeeded = true; } } if (LoadedChunkPositions.Contains(targetPosZ)) { if (chunks[ChunkIndexMapping[targetPosZ]].isLoaded && chunks.Count <= ChunkIndexMapping[targetPosZ]) { chunks[ChunkIndexMapping[targetPosZ]].UpdateNeeded = true; } } } updates.Clear(); } } #endregion #region Unloading lock (LoadedChunkPositions) { for (int i = 0; i < chunks.Count; i++) { if (chunks[i].isLoaded) { if (previousPlayerChunk != playerChunk) { Profiler.Start("Chunk Unload", Color.Red); //Calculate distance in the form of a cube Vector3Int chunkPos = new Vector3Int(chunks[i].posX, chunks[i].posY, chunks[i].posZ); //GameClient.LOGGER.info(0, "Currently dealing with chunk (" + chunkPos.X + " " + chunkPos.Y + " " + chunkPos.Z + ")!"); if (LoadedChunkPositions.Contains(chunkPos) && chunkPos.X > playerChunk.X + SimulationDistance || chunkPos.X < playerChunk.X - SimulationDistance - 1 || chunkPos.Y > playerChunk.Y + SimulationDistance || chunkPos.Y < playerChunk.Y - SimulationDistance - 1 || chunkPos.Z > playerChunk.Z + SimulationDistance || chunkPos.Z < playerChunk.Z - SimulationDistance - 1 //Max world bounds || chunkPos.X > WorldWidth || chunkPos.X < -WorldWidth - 1 || chunkPos.Y > MaxChunksY || chunkPos.Y < 0 || chunkPos.Z > WorldWidth || chunkPos.Z < -WorldWidth - 1) { UnloadChunk(chunks[i].posX, chunks[i].posY, chunks[i].posZ); } else { //Add to LoadedChunkPositions if (!LoadedChunkPositions.Contains(chunkPos)) { LoadedChunkPositions.Add(chunkPos); } } Profiler.Stop("Chunk Unload"); Profiler.Start("Chunk Manager Update", Color.MediumPurple); //Update the chunk chunks[i].Update(); } } } #endregion #region Loading //Now that we've unloaded all chunks, we can see which chunks we need to load byte numberOfChunksLoadedThisFrame = 0; //This algorithm loads all chunks on the z axis, then the x axis and lastly the y axis //It only load 1 z layer every frame (a layer is anything from 1 chunk to RENDER_DISTANCE chunks if (numberOfChunksLoaded < SimulationArea + 1) { for (int i = 0; i < LoadDistance; i++) { Profiler.Start("Chunk Load Math", Color.DarkOliveGreen); //Calculate the bounds of chunks that can be loaded int minX = MathHelper.Clamp(playerChunk.X - i, playerChunk.X - SimulationDistance, playerChunk.X + SimulationDistance); int minY = MathHelper.Clamp(playerChunk.Y - i, playerChunk.Y - SimulationDistance, playerChunk.Y + SimulationDistance); int minZ = MathHelper.Clamp(playerChunk.Z - i, playerChunk.Z - SimulationDistance, playerChunk.Z + SimulationDistance); int maxX = MathHelper.Clamp(playerChunk.X + i, playerChunk.X - SimulationDistance, playerChunk.X + SimulationDistance); int maxY = MathHelper.Clamp(playerChunk.Y + i, playerChunk.Y - SimulationDistance, playerChunk.Y + SimulationDistance); int maxZ = MathHelper.Clamp(playerChunk.Z + i, playerChunk.Z - SimulationDistance, playerChunk.Z + SimulationDistance); //World boundaries. Chunks cant be loaded outside these boundaries minX = MathHelper.Clamp(minX, -WorldWidth, WorldWidth); minY = MathHelper.Clamp(minY, 0, MaxChunksY); minZ = MathHelper.Clamp(minZ, -WorldWidth, WorldWidth); maxX = MathHelper.Clamp(maxX, -WorldWidth, WorldWidth); maxY = MathHelper.Clamp(maxY, 0, MaxChunksY); maxZ = MathHelper.Clamp(maxZ, -WorldWidth, WorldWidth); Profiler.Stop("Chunk Load Math"); for (int y = minY; y < maxY; y++) { for (int x = minX; x < maxX; x++) { for (int z = minZ; z < maxZ; z++) { Profiler.Start("Chunk Load", Color.Ivory); Vector3Int chunkPos = new Vector3Int(x, y, z); //Check if we should load this chunk (if its within simulation distance & isn't inside LoadedChunkPositions>) if (!LoadedChunkPositions.Contains(chunkPos) && chunkPos.X < playerChunk.X + SimulationDistance && chunkPos.X > playerChunk.X - SimulationDistance && chunkPos.Y < playerChunk.Y + SimulationDistance && chunkPos.Y > playerChunk.Y - SimulationDistance && chunkPos.Z < playerChunk.Z + SimulationDistance && chunkPos.Z > playerChunk.Z - SimulationDistance //Max world bounds && chunkPos.X < WorldWidth && chunkPos.X > -WorldWidth && chunkPos.Y < MaxChunksY && chunkPos.Y > -1 && chunkPos.Z < WorldWidth && chunkPos.Z > -WorldWidth) { Profiler.Stop("Chunk Load"); //Chunk is unloaded. So load it Profiler.Start("Chunk Generation", Color.DeepPink); //Chunk NextChunkToLoad = SolidChunk.Generate(chunkPos.X, chunkPos.Y, chunkPos.Z, 1); Chunk NextChunkToLoad = FetchChunk(chunkPos.X, chunkPos.Y, chunkPos.Z); RegisterChunk(NextChunkToLoad); Profiler.Stop("Chunk Generation"); Profiler.Start("Chunk Load", Color.Ivory); //RegisterChunk(FetchChunk(chunkPos.X, chunkPos.Y, chunkPos.Z)); numberOfChunksLoaded++; numberOfChunksLoadedThisFrame++; Profiler.Stop("Chunk Load"); //If we loaded more than the maximum number of chunks, stop loading chunks this frame to keep a playable framerate if (numberOfChunksLoadedThisFrame > MaximumChunksToLoadPerFrame) { break; } } else { Profiler.Stop("Chunk Load"); } } //If we loaded more than the maximum number of chunks, stop loading chunks this frame to keep a playable framerate if (numberOfChunksLoadedThisFrame > MaximumChunksToLoadPerFrame) { break; } } //Break the loop if we loaded a single chunk if (numberOfChunksLoadedThisFrame > 0) { break; } } //Break the loop if we loaded a single chunk if (numberOfChunksLoadedThisFrame > 0) { break; } } if (numberOfChunksLoadedThisFrame < 1) { LoadDistance++; } if (LoadDistance > SimulationDistance) { LoadDistance = 2; } } } previousPlayerChunk = playerChunk; #endregion #endregion #region Fixed Terrain Code /*byte numberOfChunksLoadedThisFrame = 0; * * //This algorithm loads all chunks on the z axis, then the x axis and lastly the y axis * //It only load 1 z layer every frame (a layer is anything from 1 chunk to RENDER_DISTANCE chunks * if (numberOfChunksLoaded <= SimulationArea) * { * Profiler.Start("Chunk Load Loop"); * * for (int i = 0; i < LoadDistance; i++) * { * Profiler.Start("Chunk Load Math", Color.DarkOliveGreen); * * //Calculate the bounds of chunks that can be loaded * int minX = playerChunk.X - i; * int minY = playerChunk.Y - i; * int minZ = playerChunk.Z - i; * * int maxX = playerChunk.X + i; * int maxY = playerChunk.Y + i; * int maxZ = playerChunk.Z + i; * * //World boundaries. Chunks cant be loaded outside these boundaries * minX = MathHelper.Clamp(minX, -8, 8); * minY = MathHelper.Clamp(minY, 0, 16); * minZ = MathHelper.Clamp(minZ, -8, 8); * * maxX = MathHelper.Clamp(maxX, -8, 8); * maxY = MathHelper.Clamp(maxY, 0, 16); * maxZ = MathHelper.Clamp(maxZ, -8, 8); * * Profiler.Stop("Chunk Load Math"); * * for (int y = minY; y < maxY; y++) * { * for (int x = minX; x < maxX; x++) * { * for (int z = minZ; z < maxZ; z++) * { * Profiler.Start("Chunk Load", Color.Ivory); * * Vector3Int chunkPos = new Vector3Int(x, y, z); * * //Check if we should load this chunk (if its within simulation distance & isn't inside LoadedChunkPositions>) * if (!LoadedChunkPositions.Contains(chunkPos) && * chunkPos.X < playerChunk.X + SimulationDistance && chunkPos.X > playerChunk.X - SimulationDistance * && chunkPos.Y < playerChunk.Y + SimulationDistance && chunkPos.Y > playerChunk.Y - SimulationDistance * && chunkPos.Z < playerChunk.Z + SimulationDistance && chunkPos.Z > playerChunk.Z - SimulationDistance * * //Max world bounds * && chunkPos.X < WorldWidth && chunkPos.X > -WorldWidth * && chunkPos.Y < MaxChunksY && chunkPos.Y > -1 * && chunkPos.Z < WorldWidth && chunkPos.Z > -WorldWidth) * { * //Chunk is unloaded. So load it * Profiler.Start("Chunk Generation", Color.DeepPink); * Chunk NextChunkToLoad = SolidChunk.Generate(chunkPos.X, chunkPos.Y, chunkPos.Z, 1); * RegisterChunk(NextChunkToLoad); * Profiler.Stop("Chunk Generation"); * * //RegisterChunk(FetchChunk(chunkPos.X, chunkPos.Y, chunkPos.Z)); * * numberOfChunksLoaded++; * numberOfChunksLoadedThisFrame++; * * Profiler.Stop("Chunk Load"); * * //If we loaded more than the maximum number of chunks, stop loading chunks this frame to keep a playable framerate * if (numberOfChunksLoadedThisFrame > MaximumChunksToLoadPerFrame) * { * break; * } * } * else * { * Profiler.Stop("Chunk Load"); * } * } * * //If we loaded more than the maximum number of chunks, stop loading chunks this frame to keep a playable framerate * if (numberOfChunksLoadedThisFrame > MaximumChunksToLoadPerFrame) * { * break; * } * } * * //Break the loop if we loaded a single chunk * if (numberOfChunksLoadedThisFrame > 0) * { * break; * } * } * * //Break the loop if we loaded a single chunk * if (numberOfChunksLoadedThisFrame > 0) * { * break; * } * } * * if (numberOfChunksLoadedThisFrame < 1) * { * LoadDistance++; * } * if (LoadDistance > SimulationDistance) * { * LoadDistance = 2; * } * }*/ #endregion SyncCache(); Profiler.Stop("Chunk Manager Update"); ProcessingTime = (long)DateTime.UtcNow.TimeOfDay.TotalMilliseconds - ProcessingTime; //Wait 1/60 - the time elapsed, clamped between 0 and int.max Thread.Sleep(FastMath.FastClamp((int)(50 - ProcessingTime), 50, int.MaxValue)); } } catch (ThreadAbortException ex) { //Thread aborting! Logger.fatal(-1, "Thread \"" + Thread.CurrentThread.Name + "\" aborted! This is usually normal!"); } }
public override void onUpdate() { //only handle the update if the button is enabled and the parent screen is visible if (screenVisible && render) { //hovering logic (checks if the slider is hovered) if (isMouseHovering() && selectable && enabled) { Parent.FocusedComponent = this; if (isMouseHovering(new Point(FastMath.FastClamp(location.X + (_value * size.X / (_maxValue - _minValue)), location.X, location.X + size.X - HandleWidth * GameSettings.UIScale), location.Y), new Point(HandleWidth * GameSettings.UIScale, size.Y))) { if (isHovered == false) { isHovered = true; if (OnHover != null) { OnHover.Invoke(null, null); } } } if (InputManager.IsPressed("gui.click")) { if (isClicked == false) { isClicked = true; if (OnClicked != null) { OnClicked.Invoke(null, null); } } } if (InputManager.Released("gui.click")) { if (isClicked && Parent.FocusedComponent != null && Parent.FocusedComponent == this) { isClicked = false; if (OnReleased != null) { OnReleased.Invoke(null, null); } } } if (isClicked) { if (isSliding == false) { //If we are hovering on the handle then: if (isMouseHovering(new Point(FastMath.FastClamp(location.X + (_value * size.X / (_maxValue - _minValue)), location.X, location.X + size.X - HandleWidth * GameSettings.UIScale), location.Y), new Point(HandleWidth * GameSettings.UIScale, size.Y))) { MouseOffset = new Point(handleX - InputManager.MouseX, location.Y - InputManager.MouseY); } //We are not hovering on the handle, so assume its half the width else { MouseOffset = new Point(-HandleWidth * GameSettings.UIScale / 2, location.Y - InputManager.MouseY); } isSliding = true; } } } //unhover logic else { if (isHovered) { isHovered = false; } if (!InputManager.IsPressed("gui.click")) { if (isClicked == true) { isClicked = false; if (OnClicked != null) { OnClicked.Invoke(null, null); } if (OnReleased != null) { OnReleased.Invoke(null, null); } } } } if (isClicked == false || Parent.FocusedComponent != this) { isSliding = false; } //Handle the slider position if (isSliding) { //Get the mouse relative to the component int localPosition = InputManager.MouseX - location.X; float coef = (float)(_maxValue) / (float)(size.X - 3); int value = (int)(localPosition * coef); value = FastMath.FastClamp(value, 0, _maxValue); if (value != _value) { _value = value; if (OnValueChanged != null) { OnValueChanged.Invoke(null, null); } } } } }
public override void onUpdate() { //only handle the update if the button is enabled and the parent screen is visible if (screenVisible && render) { #region Mouse Logic //hovering logic (checks if the slider is hovered) if (isMouseHovering() && selectable && enabled) { var prevPosition = caretPosition; Mouse.SetCursor(MouseCursor.IBeam); Parent.FocusedComponent = this; if (isHovered == false) { isHovered = true; if (OnHover != null) { OnHover.Invoke(null, null); } } #region Focusing Logic if (InputManager.IsPressed("gui.click")) { if (isClicked == false) { isClicked = true; if (OnClicked != null) { OnClicked.Invoke(null, null); } isEditing = true; } #region Mouse Highlighting Logic if (isEditing) { GetCaretUnderMouse(); if (selectedCaretPositionTMP == -1) { selectedCaretPositionTMP = caretPosition; } if (selectedCaretPositionTMP != caretPosition) { //is holding selectedPosition = selectedCaretPositionTMP; } } #endregion } #endregion #region Double Click Logic if (InputManager.Released("gui.click")) { if (isClicked && Parent.FocusedComponent != null && Parent.FocusedComponent == this) { isClicked = false; if (OnReleased != null) { OnReleased.Invoke(null, null); } selectedCaretPositionTMP = -1; caretTimer = 0f; //empty text boxes shouldnt crash if (text.Length > 0) { //Double click logic if (previousClickTime - timingMachine > 0f && previousClickTime - timingMachine <= 0.5f) //Check the time frame { if (selectedPosition == -1) { //If we moved more than 5 pixels from the last click in any direction then it doesnt count as a double click if (prevMouseLocation.X - 5 <= InputManager.MouseX && prevMouseLocation.X + 5 >= InputManager.MouseX && prevMouseLocation.Y - 5 <= InputManager.MouseY && prevMouseLocation.Y + 5 >= InputManager.MouseY) { GetCaretUnderMouse(); //Get the bounds of the current word int end = IndexOfNextCharAfterWhitespace(); caretPosition = end; int start = IndexOfLastCharBeforeWhitespace(); selectedPosition = start; timingMachine = 0f; previousClickTime = 0f; } } else { selectedPosition = -1; } } timingMachine = previousClickTime; } prevMouseLocation = new Point(InputManager.MouseX, InputManager.MouseY); } } #endregion if (InputManager.PressedStart("gui.click")) { selectedPosition = -1; } } #region Unhover Logic else { if (!isMouseHovering() && Parent.FocusedComponent == this) { Mouse.SetCursor(MouseCursor.Arrow); } if (isHovered) { isHovered = false; } if (!InputManager.IsPressed("gui.click")) { if (isClicked == true) { isClicked = false; if (OnClicked != null) { OnClicked.Invoke(null, null); } if (OnReleased != null) { OnReleased.Invoke(null, null); } caretTimer = 0f; isEditing = false; } } } #endregion #endregion caretTimer += Time.DeltaTime; #region Losing Focus to Another Component if (isEditing) { previousClickTime += Time.DeltaTime; } if (Parent.FocusedComponent != this) { if (Parent.FocusedComponent != null) { previousComponent = Parent.FocusedComponent; } else { isEditing = false; } } //Focusing logic (on click on unfocused = unfocused) if (InputManager.Released("gui.click")) { if (!isMouseHovering()) { isEditing = false; timingChars = 0f; } } #endregion #region Keyboard Handling //Get Textbox input if (isEditing) { bool isShiftPressed = InputManager.IsPressed("misc.shift") || InputManager.IsPressed("misc.shift.alt"); bool isControlHeld = InputManager.IsPressed("misc.control") || InputManager.IsPressed("misc.control.alt"); bool isAltHeld = InputManager.IsPressed("misc.alternate") || InputManager.IsPressed("misc.alternate.alt"); #region Misc Input Handling #region Caret Movement timingChars += Time.DeltaTime; if (InputManager.Released("misc.left") || InputManager.Released("misc.right")) { timingChars = 0f; } //Arrow hold timings if ((timingChars < 1f && timingChars % 0.5f < 0.1f) || //First second delay (timingChars > 1f && timingChars < 2f && timingChars % 0.25f < 0.1f) || //Second second delay (timingChars > 2f && timingChars < 3f && timingChars % 0.1f < 0.05f)) //Hold delay { //Movement if (InputManager.IsPressed("misc.left") && !isAltHeld) //Left Arrow { if (isShiftPressed) { if (selectedPosition == -1) { selectedPosition = FastMath.FastClamp(caretPosition, 0, text.Length); } } else { if (selectedPosition != -1) { caretPosition = Math.Min(caretPosition, selectedPosition); selectedPosition = -1; caretPosition++; } } if (isControlHeld) { caretPosition = IndexOfLastCharBeforeWhitespace() + 1; } caretPosition = FastMath.FastClamp(caretPosition - 1, 0, text.Length); //Minimum position = 0 } if (InputManager.IsPressed("misc.right") && !isAltHeld) //Right Arrow { if (isShiftPressed) { if (selectedPosition == -1) { selectedPosition = FastMath.FastClamp(caretPosition, 0, text.Length); } } else { if (selectedPosition != -1) { caretPosition = Math.Max(caretPosition, selectedPosition); selectedPosition = -1; caretPosition--; } } if (isControlHeld) { caretPosition = IndexOfNextCharAfterWhitespace() - 1; } caretPosition = FastMath.FastClamp(caretPosition + 1, 0, text.Length); //Maximum position = text.Length } } //Home key if (InputManager.Released("misc.home")) { caretPosition = 0; } //End key if (InputManager.Released("misc.end")) { caretPosition = text.Length; } #endregion #region Clipboard (Copy / Cut / Paste) //Control things if (isControlHeld) { //CTRL + A if (InputManager.Released("misc.A")) { selectedPosition = 0; caretPosition = text.Length; } //Clipboard if (InputManager.Released("misc.X")) { if (text.Length > 0) { if (selectedPosition != -1) { //Get the selected text string toCopy = text.Substring(Math.Min(selectedPosition, caretPosition), Math.Max(selectedPosition, caretPosition)); Clipboard.SetText(toCopy); //Same logic as backspace / delete Replace(Math.Min(selectedPosition, caretPosition), Math.Max(selectedPosition, caretPosition), string.Empty); if (selectedPosition < caretPosition) { caretPosition -= caretPosition - selectedPosition; } selectedPosition = -1; } } } if (InputManager.Released("misc.C")) { if (text.Length > 0) { if (selectedPosition != -1) { int min = Math.Min(selectedPosition, caretPosition); int max = Math.Max(selectedPosition, caretPosition); //Get the selected text string toCopy = text.Substring(min, max - min); Clipboard.SetText(toCopy); } } } if (InputManager.Released("misc.V")) { string toAdd = Clipboard.GetText(); //remove newlines toAdd = toAdd.Replace("\n", string.Empty); toAdd = toAdd.Replace("\r", string.Empty); toAdd = toAdd.Replace("\t", string.Empty); //Append the clipboard text if (text.Length > 0) { if (selectedPosition == -1) { text = text.Substring(0, caretPosition) + toAdd + text.Substring(caretPosition); //caretPosition += toAdd.Length; } else { Replace(Math.Min(selectedPosition, caretPosition), Math.Max(selectedPosition, caretPosition), toAdd.ToString()); if (selectedPosition < caretPosition) { caretPosition -= caretPosition - selectedPosition; } selectedPosition = -1; } } else { text += toAdd; } caretPosition += toAdd.Length; } } #endregion //Escape means lose focus if (InputManager.Released("misc.pause")) { //lose focus isEditing = false; //reset the cursor Mouse.SetCursor(MouseCursor.Arrow); //reset GameClient.CurrentCharacter = (char)0; GameClient.CurrentKey = Keys.None; return; } #region Text Removal if (GameClient.CurrentKey == Keys.Back) //Backspace { //If no highlighted text if (selectedPosition == -1 || text.Length == 0) { text = text.Substring(0, Math.Max(0, caretPosition - 1)) + text.Substring(Math.Min(caretPosition, text.Length)); caretPosition = FastMath.FastClamp(caretPosition, 0, text.Length); } else { Replace(Math.Min(selectedPosition, caretPosition), Math.Max(selectedPosition, caretPosition), string.Empty); if (selectedPosition < caretPosition) { caretPosition -= caretPosition - selectedPosition; } selectedPosition = -1; } } if (GameClient.CurrentKey == Keys.Delete) //Delete? { //If no highlighted text if (selectedPosition == -1) { text = text.Substring(0, caretPosition) + text.Substring(Math.Min(caretPosition + 1, text.Length)); caretPosition = FastMath.FastClamp(caretPosition, 0, text.Length); } else { Replace(Math.Min(selectedPosition, caretPosition), Math.Max(selectedPosition, caretPosition), string.Empty); if (selectedPosition < caretPosition) { caretPosition -= caretPosition - selectedPosition; } selectedPosition = -1; } } #endregion #endregion #region Input Handling //Appending text if (!(GameClient.CurrentKey == Keys.Back || GameClient.CurrentKey == Keys.Delete || GameClient.CurrentKey == Keys.Escape || GameClient.CurrentKey == Keys.Tab)) //If the current key isn't a printable character { #region Regular Text Input if (GameClient.CurrentKey != Keys.None) { //Append the character if (text.Length > 0) { if (selectedPosition == -1) { text = text.Substring(0, caretPosition) + GameClient.CurrentCharacter + text.Substring(caretPosition); } else { Replace(Math.Min(selectedPosition, caretPosition), Math.Max(selectedPosition, caretPosition), GameClient.CurrentCharacter.ToString()); if (selectedPosition < caretPosition) { caretPosition -= caretPosition - selectedPosition; } selectedPosition = -1; } } else { text += GameClient.CurrentCharacter; } caretPosition++; } #endregion #region Modifier Characters else if (GameClient.CurrentCharacter != '\0') { char toAdd = GameClient.CurrentCharacter; //if uppercase if (isShiftPressed) { toAdd = char.ToUpper(toAdd); } //Caps Lock #if DESKTOP if (InputManager.IsPressed("misc.capslock")) { toAdd = char.ToUpper(toAdd); } #endif //Append the character if (text.Length > 0) { if (selectedPosition == -1) { text = text.Substring(0, caretPosition) + toAdd + text.Substring(caretPosition); } else { Replace(Math.Min(selectedPosition, caretPosition), Math.Max(selectedPosition, caretPosition), toAdd.ToString()); if (selectedPosition < caretPosition) { caretPosition -= caretPosition - selectedPosition; } selectedPosition = -1; } } else { text += toAdd; } caretPosition++; } #endregion } #endregion //Reset GameClient.CurrentCharacter = (char)0; GameClient.CurrentKey = Keys.None; } #endregion else { //Reset highlighting selectedPosition = -1; } } }