From 2b72360a9bebe78feb56b4c230f6afa25cb2d1e9 Mon Sep 17 00:00:00 2001 From: guus <_@guusw.nl> Date: Wed, 11 Mar 2026 19:31:41 +0100 Subject: [PATCH] Build/editor stuff and fix mod compilation --- .nvim.lua | 96 +++++++ lua/lemon.lua | 192 ++++++++++++++ lua/snippets.lua | 33 +++ make_links | 2 +- mod/PLibBuildings/ColoredRangeVisualizer.cs | 271 -------------------- oni-prio.sln | 4 - 6 files changed, 322 insertions(+), 276 deletions(-) create mode 100644 .nvim.lua create mode 100644 lua/lemon.lua create mode 100644 lua/snippets.lua delete mode 100644 mod/PLibBuildings/ColoredRangeVisualizer.cs diff --git a/.nvim.lua b/.nvim.lua new file mode 100644 index 0000000..c843d4f --- /dev/null +++ b/.nvim.lua @@ -0,0 +1,96 @@ +require("lua/lemon") +require("lua/snippets") + +vim.opt.errorformat = table.concat({ + -- '%f(%l\\,%c):\\ %tarning\\ %m\\ [%.%#]', + '%f(%l\\,%c):\\ %trror\\ %m\\ [%.%#]', +}, ',') + +local def_workspace = { args = {}, build_type = "Debug", binary = "ng", build_target = "native", } +local workspace = InitWorkspace(def_workspace) + +local bin_target +local bin_name + +local function updateBuildEnv() + -- The run (F6) arguments + vim.opt.makeprg = "dotnet build" +end + +updateBuildEnv() + +-- Update args for both run and debug configs +local function updateArgs(args) + workspace.args = args + WriteWorkspace() +end + +local function updateTarget(tgt) + if type(tgt) ~= "string" then + vim.api.nvim_echo({ { "Invalid target", "ErrorMsg" } }, false, {}) + return + end + workspace.binary = tgt + updateBuildEnv() + WriteWorkspace() +end + +-- The Configure command +vim.api.nvim_create_user_command("Configure", function(a) + local build_type = "Debug" + local build_target = "native" + local a1 = a.fargs + if #a1 > 0 then build_type = a1[1] end + if #a1 > 1 then build_target = a1[2] end + vim.print(a1[1]) + + workspace.build_type = build_type + workspace.build_target = build_target + + updateBuildEnv() + WriteWorkspace() + + local args = {} + table.insert(args, "-B" .. build_folder) + table.insert(args, "-GNinja") + table.insert(args, "-DCMAKE_BUILD_TYPE=" .. build_type) + table.insert(args, "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON") + if workspace.build_target == 'web' then + table.insert(args, "-DCMAKE_TOOLCHAIN_FILE=cmake/Toolchains/Web.cmake") + table.insert(args, "-DTOOLS_HINT_PATH=" .. build_folder .. "/../b_Tool") + else + table.insert(args, "-DCMAKE_CXX_COMPILER=clang++") + table.insert(args, "-DCMAKE_C_COMPILER=clang") + end + -- Feed into cmd !cmake + + vim.fn.feedkeys(":!cmake " .. table.concat(args, " ")) +end, { nargs = '*', desc = "Update run/debug arguments" }) + +vim.api.nvim_create_user_command("Target", function(a) updateTarget(a.args) end, + { nargs = 1, desc = "Update run/debug target" }) + +vim.api.nvim_create_user_command("Args", function(a) updateArgs(a.fargs) end, + { nargs = '*', desc = "Update run/debug arguments" }) + +vim.api.nvim_create_user_command("ReloadWorkspace", function(a) + workspace = InitWorkspace(def_workspace) + updateBuildEnv() + end, + { nargs = 0, desc = "Reload workspace debug/run configuration from file" }) + +-- F6 to run the application +local function buildAndRun(silent) + SaveAllCode() + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, false, true), "n", true) + vim.schedule(function() + MakeAnd(function() end, silent) + end) +end +local function buildAndRunSilent() buildAndRun(true) end +vim.keymap.set('n', '', buildAndRunSilent) +vim.keymap.set('i', '', buildAndRunSilent) +vim.keymap.set('n', '', buildAndRun) +vim.keymap.set('i', '', buildAndRun) +vim.keymap.set('n', '', buildAndRun) +vim.keymap.set('i', '', buildAndRun) diff --git a/lua/lemon.lua b/lua/lemon.lua new file mode 100644 index 0000000..c13915b --- /dev/null +++ b/lua/lemon.lua @@ -0,0 +1,192 @@ +Lemon = { + ws = {}, + ws_file = '.nvim.workspace.lua', + term_buf = nil, + term_win_cmd = 'belowright 12split', +} + +function LoadWorkspace() + -- Load persistent configuration from .workspace.lua + local loaded, workspace = pcall(dofile, Lemon.ws_file) + if not loaded then return nil end + return workspace +end + +function WriteWorkspace() + -- A very minimal serializer for workspace configuration + local s = { l = "", ls = {}, i = "" } + local function w(v) s.l = s.l .. v end + local function nl() + s.ls[#s.ls + 1] = s.l; s.l = s.i; + end + local function wv(v) + local t = type(v) + if t == 'table' then + w('{'); local pi = s.i; s.i = s.i .. " " + for k1, v1 in pairs(v) do + nl(); w('['); wv(k1); w('] = '); wv(v1); w(',') + end + s.i = pi; nl(); w('}'); + elseif t == 'number' then + w(tostring(v)) + elseif t == 'string' then + w('"' .. v .. '"') + else + w(tostring(v)) + end + end + + -- Write the workspace file + w("return "); wv(Lemon.ws); nl() + vim.fn.writefile(s.ls, Lemon.ws_file) +end + +-- Loads the workspace from the file, or return the default +---@param default table +---@return table +function InitWorkspace(default) + Lemon.ws = LoadWorkspace() + if Lemon.ws == nil then + Lemon.ws = default + end + return Lemon.ws +end + +function TermShow() + local info = GetTermInfo() + + if info == nil then + -- Create new terminal buffer + vim.cmd(Lemon.term_win_cmd) + vim.cmd('terminal') + Lemon.term_buf = vim.api.nvim_get_current_buf() + -- Mark buffer so we can identify it later + vim.api.nvim_buf_set_var(Lemon.term_buf, 'lemon_terminal', true) + info = GetTermInfo() + elseif info.win == nil then + -- Buffer exists but not visible, open it + vim.cmd(Lemon.term_win_cmd) + vim.api.nvim_win_set_buf(0, Lemon.term_buf) + else + -- Window is visible, switch to it + vim.api.nvim_set_current_win(info.win) + end + return info +end + +-- Find or create persistent terminal buffer, open window, and run command +function TermRun(cmd) + local info = TermShow() + + -- Send command to terminal + vim.fn.chansend(info.job_id, '\021' .. cmd .. '\n') + vim.fn.feedkeys("G", "n") +end + +-- Get terminal buffer and job_id if valid, returns {buf, job_id, win} +-- win is nil if terminal is not currently visible +function GetTermInfo() + if Lemon.term_buf == nil or not vim.api.nvim_buf_is_valid(Lemon.term_buf) then + return nil + end + + local job_id = vim.api.nvim_buf_get_var(Lemon.term_buf, 'terminal_job_id') + + -- Find window showing the terminal buffer + local win = nil + for _, w in ipairs(vim.api.nvim_list_wins()) do + if vim.api.nvim_win_get_buf(w) == Lemon.term_buf then + win = w + break + end + end + + return { buf = Lemon.term_buf, job_id = job_id, win = win } +end + +-- Compatibility wrapper - returns window ID if terminal is visible +function SwitchToExistingTerm() + local info = GetTermInfo() + return info and info.win or nil +end + +function SaveAllCode() + -- Filetypes you want to save + local valid_ft = { + c = true, + cpp = true, + h = true, + hpp = true, + } + + -- Iterate through all buffers + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + -- Only act on listed + loaded buffers + if vim.api.nvim_buf_is_loaded(buf) and vim.bo[buf].buflisted then + local ft = vim.bo[buf].filetype + + -- If filetype matches, write the buffer + if valid_ft[ft] and vim.bo[buf].modified then + -- vim.print("Saving buffer", buf) + vim.api.nvim_buf_call(buf, function() + vim.cmd("write") + end) + end + end + end +end + +-- Runs the make command and runs the callback when it completes +function MakeAnd(run_callback, silent_) + local silent + if silent_ ~= nil then + silent = silent_ + else + silent = false + end + + -- Create a one-time autocmd that fires when make completes + local group = vim.api.nvim_create_augroup('MakeAnd', { clear = false }) + local wnd = vim.api.nvim_get_current_win() + local pos = vim.api.nvim_win_get_cursor(wnd) + vim.api.nvim_create_autocmd('QuickFixCmdPost', { + group = group, + pattern = 'make', + once = true, + callback = function() + local qf_list = vim.fn.getqflist() + local has_errors = false + + for _, item in ipairs(qf_list) do + if item.valid == 1 then + has_errors = true + break + end + end + + vim.schedule(function() + if not has_errors then + vim.api.nvim_echo({ { "Build succeeded", "Normal" } }, false, {}) + run_callback() + vim.api.nvim_win_set_cursor(wnd, pos) + else + vim.api.nvim_echo({ { "Build failed", "ErrorMsg" } }, false, {}) + end + end) + end + }) + + if silent then + vim.cmd('silent make!') + else + vim.cmd('make!') + end +end + +function TabCurrent() + return vim.fn.tabpagenr() +end + +function TabSwitch(tab) + vim.cmd('tabnext ' .. tab) +end diff --git a/lua/snippets.lua b/lua/snippets.lua new file mode 100644 index 0000000..34d97f0 --- /dev/null +++ b/lua/snippets.lua @@ -0,0 +1,33 @@ +-- Header guard snippet +vim.keymap.set('n', ',hg', function() + local function uuid() + local template = 'xxxxxxxx_xxxx_4xxx_yxxx_xxxxxxxxxxxx' + return string.gsub(template, '[xy]', function(c) + local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb) + return string.format('%X', v) + end) + end + local v = '_' .. uuid() + vim.api.nvim_put({ '#ifndef ' .. v, + "#define " .. v, + "", + "namespace ng {", + "", + "}", + "", + '#endif // ' .. v, + }, 'c', false, true) + vim.cmd("normal! 3k") +end, { desc = 'Inser header boiler plate' }) + +vim.keymap.set('n', ',xx', function() + local cmd = vim.api.nvim_replace_termcodes(":s/x/y/g", true, true, true) + vim.api.nvim_feedkeys(cmd, "n", false) +end) +vim.keymap.set('n', ',xty', function() + vim.api.nvim_feedkeys("yyp", "n", false) + local cmd = vim.api.nvim_replace_termcodes(":s/x/y/g", true, true, true) + vim.api.nvim_feedkeys(cmd, "n", false) +end) + + diff --git a/make_links b/make_links index 228e899..1840567 100755 --- a/make_links +++ b/make_links @@ -1,7 +1,7 @@ #!/bin/bash APP=~/.local/share/Steam/steamapps/common/OxygenNotIncluded -SAVE="~/.config/unity3d/Klei/Oxygen Not Included" +SAVE=~/.config/unity3d/Klei/Oxygen\ Not\ Included set -ex ln -sf "$APP/OxygenNotIncluded_Data/Managed" Libs diff --git a/mod/PLibBuildings/ColoredRangeVisualizer.cs b/mod/PLibBuildings/ColoredRangeVisualizer.cs deleted file mode 100644 index e2fe184..0000000 --- a/mod/PLibBuildings/ColoredRangeVisualizer.cs +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2022 Peter Han - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software - * and associated documentation files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -using PeterHan.PLib.Core; -using System; -using System.Collections.Generic; -using UnityEngine; - -namespace PeterHan.PLib.Buildings { - /// - /// A visualizer that colors cells with an overlay when a building is selected or being - /// previewed. - /// - public abstract class ColoredRangeVisualizer : KMonoBehaviour { - /// - /// The anim name to use when visualizing. - /// - private const string ANIM_NAME = "transferarmgrid_kanim"; - - /// - /// The animations to play when the visualization is created. - /// - private static readonly HashedString[] PRE_ANIMS = new HashedString[] { - "grid_pre", - "grid_loop" - }; - - /// - /// The animation to play when the visualization is destroyed. - /// - private static readonly HashedString POST_ANIM = "grid_pst"; - - /// - /// The layer on which to display the visualizer. - /// - public Grid.SceneLayer Layer { get; set; } - - // These components are automatically populated by KMonoBehaviour -#pragma warning disable IDE0044 // Add readonly modifier -#pragma warning disable CS0649 - [MyCmpGet] - protected BuildingPreview preview; - - [MyCmpGet] - protected Rotatable rotatable; -#pragma warning restore CS0649 -#pragma warning restore IDE0044 // Add readonly modifier - - /// - /// The cells where animations are being displayed. - /// - private readonly HashSet cells; - - protected ColoredRangeVisualizer() { - cells = new HashSet(); - Layer = Grid.SceneLayer.FXFront; - } - - /// - /// Creates or updates the visualizers as necessary. - /// - private void CreateVisualizers() { - var visCells = HashSetPool.Allocate(); - var newCells = ListPool.Allocate(); - try { - if (gameObject != null) - VisualizeCells(visCells); - // Destroy cells that are not used in the new one - foreach (var cell in cells) - if (visCells.Remove(cell)) - newCells.Add(cell); - else - cell.Destroy(); - // Newcomers get their controller created and added to the list - foreach (var newCell in visCells) { - newCell.CreateController(Layer); - newCells.Add(newCell); - } - // Copy back to global - cells.Clear(); - foreach (var cell in newCells) - cells.Add(cell); - } finally { - visCells.Recycle(); - newCells.Recycle(); - } - } - - /// - /// Called when cells are changed in the building radius. - /// - private void OnCellChange() { - CreateVisualizers(); - } - - protected override void OnCleanUp() { - Unsubscribe((int)GameHashes.SelectObject); - if (preview != null) { - Singleton.Instance.UnregisterCellChangedHandler(transform, - OnCellChange); - if (rotatable != null) - Unsubscribe((int)GameHashes.Rotated); - } - RemoveVisualizers(); - base.OnCleanUp(); - } - - /// - /// Called when the object is rotated. - /// - private void OnRotated(object _) { - CreateVisualizers(); - } - - /// - /// Called when the object is selected. - /// - /// true if selected, or false if deselected. - private void OnSelect(object data) { - if (data is bool selected) { - var position = transform.position; - // Play the appropriate sound and update the visualizers - if (selected) { - PGameUtils.PlaySound("RadialGrid_form", position); - CreateVisualizers(); - } else { - PGameUtils.PlaySound("RadialGrid_disappear", position); - RemoveVisualizers(); - } - } - } - - protected override void OnSpawn() { - base.OnSpawn(); - Subscribe((int)GameHashes.SelectObject, OnSelect); - if (preview != null) { - // Previews can be moved - Singleton.Instance.RegisterCellChangedHandler(transform, - OnCellChange, nameof(ColoredRangeVisualizer) + ".OnSpawn"); - if (rotatable != null) - Subscribe((int)GameHashes.Rotated, OnRotated); - } - } - - /// - /// Removes all of the visualizers. - /// - private void RemoveVisualizers() { - foreach (var cell in cells) - cell.Destroy(); - cells.Clear(); - } - - /// - /// Calculates the offset cell from the specified starting point, including the - /// rotation of this object. - /// - /// The starting cell. - /// The offset if the building had its default rotation. - /// The computed destination cell. - protected int RotateOffsetCell(int baseCell, CellOffset offset) { - if (rotatable != null) - offset = rotatable.GetRotatedCellOffset(offset); - return Grid.OffsetCell(baseCell, offset); - } - - /// - /// Called when cell visualizations need to be updated. Visualized cells should be - /// added to the collection supplied as an argument. - /// - /// The cells which should be visualized. - protected abstract void VisualizeCells(ICollection newCells); - - /// - /// Stores the data about a particular cell, including its anim controller and tint - /// color. - /// - protected sealed class VisCellData : IComparable { - /// - /// The target cell. - /// - public int Cell { get; } - - /// - /// The anim controller for this cell. - /// - public KBatchedAnimController Controller { get; private set; } - - /// - /// The tint used for this cell. - /// - public Color Tint { get; } - - /// - /// Creates a visualized cell. - /// - /// The cell to visualize. - public VisCellData(int cell) : this(cell, Color.white) { } - - /// - /// Creates a visualized cell. - /// - /// The cell to visualize. - /// The color to tint it. - public VisCellData(int cell, Color tint) { - Cell = cell; - Controller = null; - Tint = tint; - } - - public int CompareTo(VisCellData other) { - if (other == null) - throw new ArgumentNullException(nameof(other)); - return Cell.CompareTo(other.Cell); - } - - /// - /// Creates the anim controller for this cell. - /// - /// The layer on which to display the animation. - public void CreateController(Grid.SceneLayer sceneLayer) { - Controller = FXHelpers.CreateEffect(ANIM_NAME, Grid.CellToPosCCC(Cell, - sceneLayer), null, false, sceneLayer, true); - Controller.destroyOnAnimComplete = false; - Controller.visibilityType = KAnimControllerBase.VisibilityType.Always; - Controller.gameObject.SetActive(true); - Controller.Play(PRE_ANIMS, KAnim.PlayMode.Loop); - Controller.TintColour = Tint; - } - - /// - /// Destroys the anim controller for this cell. - /// - public void Destroy() { - if (Controller != null) { - Controller.destroyOnAnimComplete = true; - Controller.Play(POST_ANIM, KAnim.PlayMode.Once, 1f, 0f); - Controller = null; - } - } - - public override bool Equals(object obj) { - return obj is VisCellData other && other.Cell == Cell && Tint.Equals(other. - Tint); - } - - public override int GetHashCode() { - return Cell; - } - - public override string ToString() { - return "CellData[cell={0:D},color={1}]".F(Cell, Tint); - } - } - } -} diff --git a/oni-prio.sln b/oni-prio.sln index d44e42d..2a4e90d 100644 --- a/oni-prio.sln +++ b/oni-prio.sln @@ -5,10 +5,6 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "oni-prio", "mod\oni-prio.csproj", "{B5C0FB18-102D-434C-B8D7-2DDAE54367D6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp", "reference\Assembly-CSharp\Assembly-CSharp.csproj", "{501D90AF-582D-4F90-9FE6-1479FBA92A4B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp-firstpass", "reference\Assembly-CSharp-firstpass\Assembly-CSharp-firstpass.csproj", "{F0AD7234-F5CE-4A5E-8E8A-F621F0B7C0A0}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU