From 99e19b1f3976aca5f8fdb0a14d65acae23beb7a4 Mon Sep 17 00:00:00 2001 From: SicdeX Date: Mon, 17 Jun 2024 22:25:45 +0600 Subject: [PATCH] Added Menu#ImageSelectorArray --- wrapper/Data/ImageData.ts | 8 +- wrapper/Menu/DynamicImageSelector.ts | 22 +-- wrapper/Menu/ImageSelector.ts | 22 +-- wrapper/Menu/ImageSelectorArr.ts | 234 +++++++++++++++++++++++++++ wrapper/Menu/Node.ts | 38 +++-- wrapper/Menu/index.ts | 1 + 6 files changed, 297 insertions(+), 28 deletions(-) create mode 100644 wrapper/Menu/ImageSelectorArr.ts diff --git a/wrapper/Data/ImageData.ts b/wrapper/Data/ImageData.ts index b17827e5b..bffc9ca04 100644 --- a/wrapper/Data/ImageData.ts +++ b/wrapper/Data/ImageData.ts @@ -21,6 +21,7 @@ export const Paths = new (class BaseImageData { public readonly Icons = { topbar_mana: `${this.reborn}/topbar_mana_psd.vtex_c`, + invoker_cataclysm: `${this.AbilityIcons}/invoker/magus_apex/invoker_sun_strike_png.vtex_c`, topbar_health: `${this.reborn}/topbar_health_psd.vtex_c`, topbar_health_dire: `${this.reborn}/topbar_health_dire_psd.vtex_c`, topbar_health_colorblind: `${this.reborn}/topbar_health_colorblind_psd.vtex_c`, @@ -121,7 +122,12 @@ const getTexturePath = (name: string, isItem = false): string => { return abilityData.TexturePath } if (!isItem) { - return Paths.AbilityIcons + "/" + name + "_png.vtex_c" + switch (name) { + case "invoker_cataclysm": + return Paths.Icons.invoker_cataclysm + default: + return Paths.AbilityIcons + "/" + name + "_png.vtex_c" + } } name = !name.includes("recipe_") ? name.replace("item_", "") diff --git a/wrapper/Menu/DynamicImageSelector.ts b/wrapper/Menu/DynamicImageSelector.ts index ec99ceb81..4232c2d72 100644 --- a/wrapper/Menu/DynamicImageSelector.ts +++ b/wrapper/Menu/DynamicImageSelector.ts @@ -1,11 +1,16 @@ import { Color } from "../Base/Color" import { Rectangle } from "../Base/Rectangle" import { Vector2 } from "../Base/Vector2" +import { + GetHeroTexture, + GetItemTexture, + GetRuneTexture, + GetSpellTexture +} from "../Data/ImageData" import { GUIInfo } from "../GUI/GUIInfo" import { EventsSDK } from "../Managers/EventsSDK" import { InputEventSDK, InputManager, VKeys } from "../Managers/InputManager" import { RendererSDK } from "../Native/RendererSDK" -import { AbilityData } from "../Objects/DataBook/AbilityData" import { Base, IMenu } from "./Base" type IDefaultValues = Map< @@ -165,17 +170,16 @@ export class DynamicImageSelector extends Base { continue } let path = name - if (path.startsWith("rune_")) { - path = `panorama/images/spellicons/${path}_png.vtex_c` + if (!path.startsWith("npc_dota_hero_")) { + path = path.startsWith("item_") + ? GetItemTexture(path) + : GetSpellTexture(path) + } else if (path.startsWith("rune_")) { + path = GetRuneTexture(path) } else if (path.startsWith("item_bottle_")) { path = `panorama/images/items/${path.substring(5)}_png.vtex_c` - } else if (!path.startsWith("npc_dota_hero_")) { - const abil = AbilityData.GetAbilityByName(path) - if (abil !== undefined) { - path = abil.TexturePath - } } else { - path = `panorama/images/heroes/${path}_png.vtex_c` + path = GetHeroTexture(path) } const pathIamgeSize = RendererSDK.GetImageSize(path) this.imageSize.x = Math.max( diff --git a/wrapper/Menu/ImageSelector.ts b/wrapper/Menu/ImageSelector.ts index 82ed1e6a4..2c357d83c 100644 --- a/wrapper/Menu/ImageSelector.ts +++ b/wrapper/Menu/ImageSelector.ts @@ -1,10 +1,15 @@ import { Color } from "../Base/Color" import { Rectangle } from "../Base/Rectangle" import { Vector2 } from "../Base/Vector2" +import { + GetHeroTexture, + GetItemTexture, + GetRuneTexture, + GetSpellTexture +} from "../Data/ImageData" import { GUIInfo } from "../GUI/GUIInfo" import { EventsSDK } from "../Managers/EventsSDK" import { RendererSDK } from "../Native/RendererSDK" -import { AbilityData } from "../Objects/DataBook/AbilityData" import { Base, IMenu } from "./Base" // every icon: 32x32, 1x1 border @@ -115,17 +120,16 @@ export class ImageSelector extends Base { for (let index = 0, end = values.length; index < end; index++) { let path = values[index] - if (path.startsWith("rune_")) { - path = `panorama/images/spellicons/${path}_png.vtex_c` + if (!path.startsWith("npc_dota_hero_")) { + path = path.startsWith("item_") + ? GetItemTexture(path) + : GetSpellTexture(path) + } else if (path.startsWith("rune_")) { + path = GetRuneTexture(path) } else if (path.startsWith("item_bottle_")) { path = `panorama/images/items/${path.substring(5)}_png.vtex_c` - } else if (!path.startsWith("npc_dota_hero_")) { - const abil = AbilityData.GetAbilityByName(path) - if (abil !== undefined) { - path = abil.TexturePath - } } else { - path = `panorama/images/heroes/${path}_png.vtex_c` + path = GetHeroTexture(path) } const pathIamgeSize = RendererSDK.GetImageSize(path) this.imageSize.x = Math.max( diff --git a/wrapper/Menu/ImageSelectorArr.ts b/wrapper/Menu/ImageSelectorArr.ts new file mode 100644 index 000000000..8d5a309e6 --- /dev/null +++ b/wrapper/Menu/ImageSelectorArr.ts @@ -0,0 +1,234 @@ +import { Color } from "../Base/Color" +import { Rectangle } from "../Base/Rectangle" +import { Vector2 } from "../Base/Vector2" +import { + GetHeroTexture, + GetItemTexture, + GetRuneTexture, + GetSpellTexture +} from "../Data/ImageData" +import { GUIInfo } from "../GUI/GUIInfo" +import { EventsSDK } from "../Managers/EventsSDK" +import { RendererSDK } from "../Native/RendererSDK" +import { Base, IMenu } from "./Base" + +// every icon: 32x32, 1x1 border +export class ImageSelectorArray extends Base { + public static OnWindowSizeChanged(): void { + ImageSelectorArray.imageBorderWidth = GUIInfo.ScaleWidth(2) + ImageSelectorArray.imageGap = GUIInfo.ScaleWidth(2) + ImageSelectorArray.baseImageHeight = GUIInfo.ScaleHeight(32) + ImageSelectorArray.randomHeightValue = GUIInfo.ScaleHeight(40) + } + + private static imageBorderWidth = 0 + private static imageGap = 0 + private static baseImageHeight = 0 + private static randomHeightValue = 0 + private static readonly elementsPerRow = 5 + private static readonly imageActivatedBorderColor = new Color(104, 4, 255) + + public enabledValues: [string, boolean][] + protected readonly imageSize = new Vector2() + protected renderedPaths: string[] = [] + + constructor( + parent: IMenu, + name: string, + public values: string[], + public readonly defaultValues: [string, boolean][] = [], + tooltip = "" + ) { + super(parent, name, tooltip) + this.enabledValues = defaultValues + } + + // TODO: check current state + public get IsZeroSelected(): boolean { + const arr = this.enabledValues + for (let index = arr.length - 1; index > -1; index--) { + const [, state] = arr[index] + if (state) { + return false + } + } + return true + } + + public get IconsRect() { + const basePos = this.Position.Add(this.textOffset).AddScalarY(this.nameSize.y + 3) + return new Rectangle( + basePos, + basePos + .Add( + this.imageSize + .AddScalar( + ImageSelectorArray.imageBorderWidth * 2 + + ImageSelectorArray.imageGap + ) + .MultiplyScalarX( + Math.min( + this.values.length, + ImageSelectorArray.elementsPerRow + ) + ) + .MultiplyScalarY( + Math.ceil( + this.values.length / ImageSelectorArray.elementsPerRow + ) + ) + ) + .SubtractScalar( + (ImageSelectorArray.elementsPerRow - 1) * ImageSelectorArray.imageGap + ) + ) + } + + public get ConfigValue() { + return this.enabledValues + } + public set ConfigValue(value) { + if ( + this.ShouldIgnoreNewConfigValue || + value === undefined || + !Array.isArray(value) + ) { + return + } + this.enabledValues = value + } + + public get ClassPriority(): number { + return 6 + } + + public Update(): boolean { + if (!super.Update()) { + return false + } + + const values = this.values + this.imageSize.x = this.imageSize.y = ImageSelectorArray.baseImageHeight + this.renderedPaths = [] + + for (let index = 0, end = values.length; index < end; index++) { + let path = values[index] + if (!path.startsWith("npc_dota_hero_")) { + path = path.startsWith("item_") + ? GetItemTexture(path) + : GetSpellTexture(path) + } else if (path.startsWith("rune_")) { + path = GetRuneTexture(path) + } else if (path.startsWith("item_bottle_")) { + path = `panorama/images/items/${path.substring(5)}_png.vtex_c` + } else { + path = GetHeroTexture(path) + } + const pathIamgeSize = RendererSDK.GetImageSize(path) + this.imageSize.x = Math.max( + this.imageSize.x, + ImageSelectorArray.baseImageHeight * (pathIamgeSize.x / pathIamgeSize.y) + ) + this.renderedPaths.push(path) + } + this.Size.x = + Math.max( + this.nameSize.x, + Math.min(this.values.length, ImageSelectorArray.elementsPerRow) * + (this.imageSize.x + + ImageSelectorArray.imageBorderWidth * 2 + + ImageSelectorArray.imageGap) + ) + + this.textOffset.x * 2 + this.Size.y = + Math.ceil(this.values.length / ImageSelectorArray.elementsPerRow) * + (this.imageSize.y + + ImageSelectorArray.imageBorderWidth * 2 + + ImageSelectorArray.imageGap) + + ImageSelectorArray.randomHeightValue + return true + } + + public IsEnabled(value: string): boolean { + return this.enabledValues.some(([name, state]) => name === value && state) + } + + public IsEnabledID(id: number): boolean { + return this.IsEnabled(this.values[id]) + } + + public Render(): void { + super.Render() + this.RenderTextDefault(this.Name, this.Position.Add(this.textOffset)) + const basePos = this.IconsRect.pos1 + for (let index = 0, end = this.values.length; index < end; index++) { + const imagePath = this.renderedPaths[index] + if (imagePath === undefined) { + continue + } + const size = this.imageSize, + pos = new Vector2( + index % ImageSelectorArray.elementsPerRow, + Math.floor(index / ImageSelectorArray.elementsPerRow) + ) + .Multiply( + this.imageSize.AddScalar( + ImageSelectorArray.imageBorderWidth * 2 + + ImageSelectorArray.imageGap + ) + ) + .Add(basePos) + + RendererSDK.Image( + imagePath, + pos, + -1, + size, + Color.White, + 0, + undefined, + !this.IsEnabled(this.values[index]) + ) + + if (this.IsEnabled(this.values[index])) { + RendererSDK.OutlinedRect( + pos, + size, + ImageSelectorArray.imageBorderWidth, + ImageSelectorArray.imageActivatedBorderColor + ) + } + } + } + + public OnMouseLeftDown(): boolean { + return !this.IconsRect.Contains(this.MousePosition) + } + + public OnMouseLeftUp(): boolean { + const rect = this.IconsRect + if (!rect.Contains(this.MousePosition)) { + return false + } + const off = rect.GetOffset(this.MousePosition) + for (let i = 0, end = this.values.length; i < end; i++) { + const basePos = new Vector2( + i % ImageSelectorArray.elementsPerRow, + Math.floor(i / ImageSelectorArray.elementsPerRow) + ).Multiply( + this.imageSize.AddScalar( + ImageSelectorArray.imageBorderWidth * 2 + ImageSelectorArray.imageGap + ) + ) + if (!new Rectangle(basePos, basePos.Add(this.imageSize)).Contains(off)) { + continue + } + this.enabledValues[i][1] = !this.IsEnabled(this.values[i]) + this.TriggerOnValueChangedCBs() + break + } + return false + } +} + +EventsSDK.on("WindowSizeChanged", () => ImageSelectorArray.OnWindowSizeChanged()) diff --git a/wrapper/Menu/Node.ts b/wrapper/Menu/Node.ts index e52563823..c22f434bd 100644 --- a/wrapper/Menu/Node.ts +++ b/wrapper/Menu/Node.ts @@ -13,6 +13,7 @@ import { Dropdown } from "./Dropdown" import { DummyJson } from "./DummyJson" import { DynamicImageSelector } from "./DynamicImageSelector" import { ImageSelector } from "./ImageSelector" +import { ImageSelectorArray } from "./ImageSelectorArr" import { IMenuParticlePicker } from "./ITypes" import { KeyBind } from "./KeyBind" import { Slider } from "./Slider" @@ -486,17 +487,39 @@ export class Node extends Base { public AddImageSelector( name: string, values: string[], - defaultValues = new Map(), - tooltip = "", + defaultValues?: Map, + tooltip?: string, + createdDefaultState?: boolean, + priority?: number + ): ImageSelector + public AddImageSelector( + name: string, + values: string[], + defaultValues?: [string, boolean][], + tooltip?: string, + createdDefaultState?: boolean, + priority?: number + ): ImageSelectorArray + public AddImageSelector( + name: string, + values: string[], + defaultValues?: Map | [string, boolean][], + tooltip: string = "", createdDefaultState = false, - priority = 0 - ) { + priority: number = 0 + ): ImageSelector | ImageSelectorArray { + if (Array.isArray(defaultValues)) { + return this.AddEntry( + new ImageSelectorArray(this, name, values, defaultValues, tooltip), + priority + ) + } return this.AddEntry( new ImageSelector( this, name, values, - defaultValues, + defaultValues ?? new Map(), tooltip, createdDefaultState ), @@ -628,10 +651,7 @@ export class Node extends Base { return this.AddEntry(new ColorPicker(this, name, defaultColor, tooltip), priority) } - public AddDummyJson( - name: string, - defaultValue: T - ): DummyJson { + public AddDummyJson(name: string, defaultValue: T): DummyJson { return this.AddEntry(new DummyJson(this, name, defaultValue), 0) } diff --git a/wrapper/Menu/index.ts b/wrapper/Menu/index.ts index 9ba7c266f..2b7b43991 100644 --- a/wrapper/Menu/index.ts +++ b/wrapper/Menu/index.ts @@ -7,6 +7,7 @@ export { ColorPicker } from "./ColorPicker" export { Dropdown } from "./Dropdown" export { DynamicImageSelector } from "./DynamicImageSelector" export { ImageSelector } from "./ImageSelector" +export { ImageSelectorArray } from "./ImageSelectorArr" export * from "./ITypes" export { KeyBind } from "./KeyBind" export { Localization } from "./Localization"