From 02457e1da48ec76ae976d4f5d6bfac2132f34321 Mon Sep 17 00:00:00 2001
From: Cerwym <1760289+Cerwym@users.noreply.github.com>
Date: Sun, 22 Mar 2026 18:39:12 +0000
Subject: [PATCH 01/15] build: Add build infrastructure and cmake modules
Introduces build/ directory organization:
- CMake toolchain files for MinGW cross-compilation (mingw32.cmake, mingw64.cmake)
- Version header template (ver_defs.h.in) for auto-generated version info
- Windows manifest file (keeperfx.exe.manifest)
- Make-based package build scripts for packaging workflow
Foundation for modular build system with cross-platform support.
---
.gitignore | 1 -
build/cmake/mingw32.cmake | 9 +
build/cmake/mingw64.cmake | 9 +
build/keeperfx.exe.manifest | 33 +++
build/make/package.mk | 163 ++++++++++++++
build/make/pkg_gfx.mk | 399 +++++++++++++++++++++++++++++++++
build/make/pkg_lang.mk | 232 +++++++++++++++++++
build/make/pkg_sfx.mk | 130 +++++++++++
build/make/prebuilds.mk | 30 +++
build/make/tool_dkillconv.mk | 96 ++++++++
build/make/tool_png2bestpal.mk | 112 +++++++++
build/make/tool_png2ico.mk | 96 ++++++++
build/make/tool_pngpal2raw.mk | 96 ++++++++
build/make/tool_po2ngdat.mk | 110 +++++++++
build/make/tool_rnctools.mk | 96 ++++++++
build/make/tool_sndbanker.mk | 96 ++++++++
build/make/version.mk | 6 +
build/ver_defs.h.in | 7 +
18 files changed, 1720 insertions(+), 1 deletion(-)
create mode 100644 build/cmake/mingw32.cmake
create mode 100644 build/cmake/mingw64.cmake
create mode 100644 build/keeperfx.exe.manifest
create mode 100644 build/make/package.mk
create mode 100644 build/make/pkg_gfx.mk
create mode 100644 build/make/pkg_lang.mk
create mode 100644 build/make/pkg_sfx.mk
create mode 100644 build/make/prebuilds.mk
create mode 100644 build/make/tool_dkillconv.mk
create mode 100644 build/make/tool_png2bestpal.mk
create mode 100644 build/make/tool_png2ico.mk
create mode 100644 build/make/tool_pngpal2raw.mk
create mode 100644 build/make/tool_po2ngdat.mk
create mode 100644 build/make/tool_rnctools.mk
create mode 100644 build/make/tool_sndbanker.mk
create mode 100644 build/make/version.mk
create mode 100644 build/ver_defs.h.in
diff --git a/.gitignore b/.gitignore
index 12a85c294f..391a7776c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,7 +14,6 @@
/config/fxdata/*.dat
/config/fxdata/*.fon
.vs/
-build*/
/out/*
*.cache
.vscode
diff --git a/build/cmake/mingw32.cmake b/build/cmake/mingw32.cmake
new file mode 100644
index 0000000000..c3380596d8
--- /dev/null
+++ b/build/cmake/mingw32.cmake
@@ -0,0 +1,9 @@
+set(CMAKE_SYSTEM_NAME Windows)
+set(TOOLCHAIN_PREFIX i686-w64-mingw32)
+set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc-posix)
+set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++-posix)
+set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres)
+set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
diff --git a/build/cmake/mingw64.cmake b/build/cmake/mingw64.cmake
new file mode 100644
index 0000000000..943858d91b
--- /dev/null
+++ b/build/cmake/mingw64.cmake
@@ -0,0 +1,9 @@
+set(CMAKE_SYSTEM_NAME Windows)
+set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)
+set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc-posix)
+set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++-posix)
+set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres)
+set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX})
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
diff --git a/build/keeperfx.exe.manifest b/build/keeperfx.exe.manifest
new file mode 100644
index 0000000000..8014da4984
--- /dev/null
+++ b/build/keeperfx.exe.manifest
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true/pm
+ permonitorv2,permonitor
+
+
+
+
diff --git a/build/make/package.mk b/build/make/package.mk
new file mode 100644
index 0000000000..04f80f74fe
--- /dev/null
+++ b/build/make/package.mk
@@ -0,0 +1,163 @@
+#******************************************************************************
+# Free implementation of Bullfrog's Dungeon Keeper strategy game.
+#******************************************************************************
+# @file package.mk
+# A script used by GNU Make to recompile the project.
+# @par Purpose:
+# Defines make rules for package with release of KeeperFX.
+# @par Comment:
+# None.
+# @author Tomasz Lis
+# @date 01 Jul 2011 - 01 Jul 2011
+# @par Copying and copyrights:
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+#******************************************************************************
+empty =
+space = $(empty) $(empty)
+PKG_NAME = pkg/keeperfx-$(subst $(space),_,$(subst .,_,$(VER_STRING)))-patch.7z
+PKG_CAMPAIGN_FILES = \
+ $(patsubst %,pkg/campgns/campgn_order.txt,$(CAMPAIGNS)) \
+ $(patsubst %,pkg/campgns/%.cfg,$(CAMPAIGNS)) \
+ $(patsubst %,pkg/%,$(foreach campaign,$(CAMPAIGNS),$(wildcard campgns/$(campaign)/*.txt))) \
+ $(patsubst %,pkg/%,$(foreach campaign,$(CAMPAIGNS),$(wildcard campgns/$(campaign)_crtr/*.cfg))) \
+ $(patsubst %,pkg/%,$(foreach campaign,$(CAMPAIGNS),$(wildcard campgns/$(campaign)_lnd/*.txt)))
+PKG_CAMPAIGN_DIRS = $(sort $(dir $(PKG_CAMPAIGN_FILES)))
+PKG_CREATURE_FILES = $(patsubst config/creatrs/%,pkg/creatrs/%,$(wildcard config/creatrs/*.cfg))
+PKG_FXDATA_FILES = \
+ $(patsubst config/fxdata/%,pkg/fxdata/%,$(wildcard config/fxdata/*.cfg)) \
+ $(patsubst config/fxdata/%,pkg/fxdata/%,$(wildcard config/fxdata/*.toml)) \
+ $(patsubst config/fxdata/%,pkg/fxdata/%,$(wildcard config/fxdata/*.txt)) \
+ $(patsubst config/%,pkg/%,$(wildcard config/fxdata/lua/**/*.lua)) \
+ pkg/fxdata/lua/init.lua
+PKG_FXDATA_DIRS = $(sort $(dir $(PKG_FXDATA_FILES)))
+PKG_MOD_FILES := $(patsubst config/%,pkg/%,$(shell find config/mods -type f))
+PKG_MOD_DIRS := $(sort $(dir $(PKG_MOD_FILES)))
+
+PKG_MAPPACK_FILES = \
+ $(patsubst %,pkg/levels/mappck_order.txt,$(MAPPACKS)) \
+ $(patsubst %,pkg/levels/%.cfg,$(MAPPACKS)) \
+ $(patsubst %,pkg/%,$(foreach mappack,$(MAPPACKS),$(wildcard levels/$(mappack)/*.cfg))) \
+ $(patsubst %,pkg/%,$(foreach mappack,$(MAPPACKS),$(filter-out %/readme.txt,$(wildcard levels/$(mappack)/*.txt)))) \
+ $(patsubst %,pkg/%,$(foreach mappack,$(MAPPACKS),$(filter-out %/readme.txt,$(wildcard levels/$(mappack)/*.toml)))) \
+ $(patsubst %,pkg/%,$(foreach mappack,$(MAPPACKS),$(filter-out %/readme.txt,$(wildcard levels/$(mappack)/*.cfg)))) \
+ $(patsubst %,pkg/%,$(foreach mappack,$(MAPPACKS),$(wildcard levels/$(mappack)_crtr/*.cfg))) \
+ $(patsubst %,pkg/%,$(foreach mappack,$(MAPPACKS),$(wildcard levels/$(mappack)_cfgs/*.cfg))) \
+ $(patsubst %,pkg/%,$(foreach mappack,$(MAPPACKS),$(wildcard levels/$(mappack)_cfgs/*.toml)))
+PKG_MAPPACK_DIRS = $(sort $(dir $(PKG_MAPPACK_FILES)))
+PKG_BIN = pkg/$(notdir $(BIN))
+PKG_BIN_MAP = $(PKG_BIN:%.exe=%.map)
+PKG_HVLOGBIN = pkg/$(notdir $(HVLOGBIN))
+PKG_HVLOGBIN_MAP = $(PKG_HVLOGBIN:%.exe=%.map)
+PKG_DOCS = pkg/keeperfx_readme.txt
+PKG_DLL = \
+ pkg/SDL2_net.dll \
+ pkg/SDL2_mixer.dll \
+ pkg/SDL2_image.dll \
+ pkg/SDL2.dll
+PKG_FILES = \
+ $(PKG_CAMPAIGN_FILES) \
+ $(PKG_CREATURE_FILES) \
+ $(PKG_FXDATA_FILES) \
+ $(PKG_MOD_FILES) \
+ $(PKG_MAPPACK_FILES) \
+ $(NGTEXTDATS) \
+ $(NCTEXTDATS) \
+ $(MPTEXTDATS) \
+ pkg/keeperfx.cfg \
+ $(PKG_BIN) \
+ $(PKG_BIN_MAP) \
+ $(PKG_HVLOGBIN) \
+ $(PKG_HVLOGBIN_MAP) \
+ $(PKG_DOCS) \
+ $(PKG_DLL)
+
+.PHONY: package
+
+pkg pkg/creatrs pkg/fxdata pkg/campgns pkg/fxdata/lua $(PKG_MAPPACK_DIRS) $(PKG_CAMPAIGN_DIRS) $(PKG_FXDATA_DIRS) $(PKG_MOD_DIRS):
+ $(MKDIR) $@
+
+pkg/fxdata/lua/%.lua: config/fxdata/lua/%.lua
+ @mkdir -p $(dir $@)
+ $(CP) $^ $@
+
+pkg/fxdata/lua/init.lua: config/fxdata/lua/init.lua | pkg/fxdata/lua
+ @mkdir -p $(dir $@)
+ $(CP) $< $@
+
+pkg/keeperfx.cfg: config/keeperfx.cfg | pkg
+ $(CP) $^ $@
+
+$(PKG_BIN): $(BIN) | pkg
+ $(CP) $^ $@
+
+$(PKG_HVLOGBIN): $(HVLOGBIN) | pkg
+ $(CP) $^ $@
+
+$(PKG_BIN_MAP): $(BIN) | pkg
+ $(CP) $(BIN:%.exe=%.map) $@
+
+$(PKG_HVLOGBIN_MAP): $(HVLOGBIN) | pkg
+ $(CP) $(HVLOGBIN:%.exe=%.map) $@
+
+pkg/%.txt: docs/%.txt | pkg
+ $(CP) $^ $@
+
+pkg/campgns/%.cfg: campgns/%.cfg | pkg/campgns
+ $(CP) $^ $@
+
+pkg/campgns/%.txt: campgns/%.txt | $(PKG_CAMPAIGN_DIRS)
+ $(CP) $^ $@
+
+pkg/creatrs/%.cfg: config/creatrs/%.cfg | pkg/creatrs
+ $(CP) $^ $@
+
+pkg/fxdata/%.cfg: config/fxdata/%.cfg | pkg/fxdata
+ $(CP) $^ $@
+
+pkg/fxdata/%.toml: config/fxdata/%.toml | pkg/fxdata
+ $(CP) $^ $@
+
+pkg/fxdata/%.txt: config/fxdata/%.txt | pkg/fxdata
+ $(CP) $^ $@
+
+pkg/fxdata/lua/%.lua: config/fxdata/lua/%.lua | pkg/fxdata/lua
+ $(CP) $^ $@
+
+pkg/fxdata/lua/lib/%.lua: config/fxdata/lua/lib/%.lua | pkg/fxdata/lua/lib
+ $(CP) $^ $@
+
+pkg/fxdata/lua/class/%.lua: config/fxdata/lua/class/%.lua | pkg/fxdata/lua/class
+ $(CP) $^ $@
+
+pkg/mods/%: config/mods/% | $(PKG_MOD_DIRS)
+ $(CP) $< $@
+
+pkg/levels/%.cfg: levels/%.cfg | $(PKG_MAPPACK_DIRS)
+ $(CP) $^ $@
+
+pkg/levels/%.txt: levels/%.txt | $(PKG_MAPPACK_DIRS)
+ $(CP) $^ $@
+
+pkg/SDL2_net.dll: sdl/for_final_package/SDL2_net.dll | pkg
+ $(CP) $^ $@
+
+pkg/SDL2_mixer.dll: sdl/for_final_package/SDL2_mixer.dll | pkg
+ $(CP) $^ $@
+
+pkg/SDL2_image.dll: sdl/for_final_package/SDL2_image.dll | pkg
+ $(CP) $^ $@
+
+pkg/SDL2.dll: sdl/for_final_package/SDL2.dll | pkg
+ $(CP) $^ $@
+
+$(PKG_NAME): $(PKG_FILES) | pkg
+ $(RM) $@ && cd $(dir $(PKG_NAME)) && 7z a $(notdir $(PKG_NAME)) $(patsubst pkg/%,%,$^) >/dev/null
+
+package: $(PKG_NAME)
+
+clean-package:
+ $(RM) -r pkg
diff --git a/build/make/pkg_gfx.mk b/build/make/pkg_gfx.mk
new file mode 100644
index 0000000000..5551dac638
--- /dev/null
+++ b/build/make/pkg_gfx.mk
@@ -0,0 +1,399 @@
+#******************************************************************************
+# Free implementation of Bullfrog's Dungeon Keeper strategy game.
+#******************************************************************************
+# @file pkg_gfx.mk
+# A script used by GNU Make to recompile the project.
+# @par Purpose:
+# Defines make rules for tools needed to build KeeperFX.
+# Most tools can either by compiled from source or downloaded.
+# @par Comment:
+# None.
+# @author Tomasz Lis
+# @date 25 Jan 2009 - 02 Jul 2011
+# @par Copying and copyrights:
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+#******************************************************************************
+
+LANDVIEWRAWS = \
+$(foreach num,00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21,pkg/campgns/keeporig_lnd/rgmap$(num).raw pkg/campgns/keeporig_lnd/viframe$(num).dat) \
+$(foreach num,00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21,pkg/campgns/ancntkpr_lnd/rgmap$(num).raw pkg/campgns/ancntkpr_lnd/viframe$(num).dat) \
+$(foreach num,00 01 02 03 04 05 06 07 08 09,pkg/campgns/burdnimp_lnd/rgmap$(num).raw pkg/campgns/burdnimp_lnd/viframe$(num).dat) \
+$(foreach num,00 01 02 03 04 05 06 07,pkg/campgns/dzjr06lv_lnd/rgmap$(num).raw pkg/campgns/dzjr06lv_lnd/viframe$(num).dat) \
+$(foreach num,00 01 02 03 04 05 06 07 08 09 10,pkg/campgns/jdkmaps8_lnd/rgmap$(num).raw pkg/campgns/jdkmaps8_lnd/viframe$(num).dat) \
+$(foreach num,00 01 02 03 04 05 06 07 08 09 10 11 12,pkg/campgns/lqizgood_lnd/rgmap$(num).raw pkg/campgns/lqizgood_lnd/viframe$(num).dat) \
+$(foreach num,00 01 02 03 04 05 06 07 08,pkg/campgns/postanck_lnd/rgmap$(num).raw pkg/campgns/postanck_lnd/viframe$(num).dat) \
+$(foreach num,00 01 02 03 04 05 06 07,pkg/campgns/pstunded_lnd/rgmap$(num).raw pkg/campgns/pstunded_lnd/viframe$(num).dat) \
+$(foreach num,00 01 02 03 04 05 06 07,pkg/campgns/twinkprs_lnd/rgmap$(num).raw pkg/campgns/twinkprs_lnd/viframe$(num).dat) \
+$(foreach num,00 01 02 03 04 05 06 07,pkg/campgns/undedkpr_lnd/rgmap$(num).raw pkg/campgns/undedkpr_lnd/viframe$(num).dat)
+
+LANDVIEWDATTABS = \
+pkg/ldata/lndflag_ens.dat \
+pkg/ldata/netflag_ens.dat \
+pkg/ldata/lndflag_pin.dat \
+pkg/ldata/netflag_pin.dat \
+pkg/ldata/maphand.dat \
+pkg/ldata/netfont.dat
+
+TOTRUREGFX = \
+pkg/ldata/torture.raw \
+pkg/ldata/door01.dat \
+pkg/ldata/door02.dat \
+pkg/ldata/door03.dat \
+pkg/ldata/door04.dat \
+pkg/ldata/door05.dat \
+pkg/ldata/door06.dat \
+pkg/ldata/door07.dat \
+pkg/ldata/door08.dat \
+pkg/ldata/door09.dat \
+pkg/ldata/fronttor.dat
+
+FRONTENDGFX = \
+pkg/data/legal32.raw \
+pkg/data/legal64.raw \
+pkg/data/legal-720p-wide.raw \
+pkg/data/legal-1080p-wide.raw \
+pkg/data/startfx32.raw \
+pkg/data/startfx64.raw \
+pkg/data/loading32.raw \
+pkg/data/loading64.raw \
+pkg/data/nocd.raw \
+pkg/ldata/front.raw \
+pkg/ldata/frontft1.dat \
+pkg/ldata/frontft2.dat \
+pkg/ldata/frontft3.dat \
+pkg/ldata/frontft4.dat \
+pkg/ldata/frontbit.dat
+
+ENGINEGFX = \
+pkg/data/creature.jty \
+pkg/data/frac00.raw \
+pkg/data/frac01.raw \
+pkg/data/frac02.raw \
+pkg/data/frac03.raw \
+pkg/data/frac04.raw \
+pkg/data/frac05.raw \
+pkg/data/frac06.raw \
+pkg/data/frac07.raw \
+pkg/data/frac08.raw \
+pkg/data/gui2-64.dat \
+pkg/data/gui2-32.dat \
+pkg/data/gui1-64.dat \
+pkg/data/gui1-32.dat \
+pkg/data/pointer64.dat \
+pkg/data/font1-64.dat \
+pkg/data/font1-32.dat \
+pkg/data/font2-32.dat \
+pkg/data/font2-64.dat \
+pkg/data/tmapa000.dat \
+pkg/data/tmapa001.dat \
+pkg/data/tmapa002.dat \
+pkg/data/tmapa003.dat \
+pkg/data/tmapa004.dat \
+pkg/data/tmapa005.dat \
+pkg/data/tmapa006.dat \
+pkg/data/tmapa007.dat \
+pkg/data/tmapa008.dat \
+pkg/data/tmapa009.dat \
+pkg/data/tmapa010.dat \
+pkg/data/tmapa011.dat \
+pkg/data/tmapa012.dat \
+pkg/data/tmapa013.dat \
+pkg/data/tmapb000.dat \
+pkg/data/tmapb001.dat \
+pkg/data/tmapb002.dat \
+pkg/data/tmapb003.dat \
+pkg/data/tmapb004.dat \
+pkg/data/tmapb005.dat \
+pkg/data/tmapb006.dat \
+pkg/data/tmapb007.dat \
+pkg/data/tmapb008.dat \
+pkg/data/tmapb009.dat \
+pkg/data/tmapb010.dat \
+pkg/data/tmapb011.dat \
+pkg/data/tmapb012.dat \
+pkg/data/tmapb013.dat \
+pkg/data/swipe01.dat \
+pkg/data/swipe02.dat \
+pkg/data/swipe03.dat \
+pkg/data/swipe04.dat \
+pkg/data/swipe05.dat \
+pkg/data/swipe06.dat \
+pkg/data/swipe07.dat \
+pkg/data/gmap64.raw \
+pkg/data/gmap32.raw \
+pkg/data/gmapbug.dat
+
+GUIDATTABS = $(LANDVIEWDATTABS) $(TOTRUREDATTABS) $(ENGINEDATTABS)
+
+.PHONY: pkg-gfx pkg-landviews pkg-menugfx pkg-enginegfx
+
+pkg-gfx: pkg-landviews pkg-menugfx pkg-enginegfx
+
+pkg-landviews: $(LANDVIEWRAWS) $(LANDVIEWDATTABS)
+
+pkg-menugfx: $(TOTRUREGFX) $(FRONTENDGFX)
+
+pkg-enginegfx: $(ENGINEGFX)
+
+pkg-landviewtabs: $(LANDVIEWDATTABS)
+
+# Creation of land view image files for campaigns
+define define_campaign_landview_rule
+pkg/campgns/$(1)_lnd/rgmap%.pal: gfx/landviews/$(1)_lnd/rgmap%.png gfx/landviews/$(1)_lnd/viframe.png tools/png2bestpal/res/color_tbl_landview.txt $$(PNGTOBSPAL)
+ -$$(ECHO) 'Building land view palette: $$@'
+ @$$(MKDIR) $$(@D)
+ $$(PNGTOBSPAL) -o "$$@" -m "$$(word 3,$$^)" "$$(word 1,$$^)" "$$(word 2,$$^)"
+ -$$(ECHO) 'Finished building: $$@'
+ -$$(ECHO) ' '
+
+pkg/campgns/$(1)_lnd/rgmap%.raw: gfx/landviews/$(1)_lnd/rgmap%.png pkg/campgns/$(1)_lnd/rgmap%.pal $$(PNGTORAW) $$(RNC)
+ -$$(ECHO) 'Building land view image: $$@'
+ $$(PNGTORAW) -o "$$@" -p "$$(word 2,$$^)" -f raw -l 100 "$$<"
+ -$$(RNC) "$$@"
+ -$$(ECHO) 'Finished building: $$@'
+ -$$(ECHO) ' '
+
+pkg/campgns/$(1)_lnd/viframe%.dat: gfx/landviews/$(1)_lnd/viframe.png pkg/campgns/$(1)_lnd/rgmap%.pal $$(PNGTORAW) $$(RNC)
+ -$$(ECHO) 'Building land view frame: $$@'
+ $$(PNGTORAW) -o "$$@" -p "$$(word 2,$$^)" -f hspr -l 50 "$$<"
+ -$$(RNC) "$$@"
+ -$$(ECHO) 'Finished building: $$@'
+ -$$(ECHO) ' '
+
+# mark palette files precious to make sure they're not auto-removed after dependencies are built
+.PRECIOUS: pkg/campgns/$(1)_lnd/rgmap%.pal
+endef
+
+$(foreach campaign,$(sort $(CAMPAIGNS)),$(eval $(call define_campaign_landview_rule,$(campaign))))
+
+pkg/ldata/torture.pal: gfx/menufx/torturescr/tortr_background.png gfx/menufx/torturescr/tortr_doora_open11.png gfx/menufx/torturescr/tortr_doorb_open11.png gfx/menufx/torturescr/tortr_doorc_open11.png gfx/menufx/torturescr/tortr_doord_open11.png gfx/menufx/torturescr/tortr_doore_open11.png gfx/menufx/torturescr/tortr_doorf_open11.png gfx/menufx/torturescr/tortr_doorg_open11.png gfx/menufx/torturescr/tortr_doorh_open11.png gfx/menufx/torturescr/tortr_doori_open11.png gfx/menufx/torturescr/cursor_horny.png tools/png2bestpal/res/color_tbl_basic.txt $(PNGTOBSPAL)
+pkg/data/legal32.pal: gfx/menufx/loading/legal-32.png tools/png2bestpal/res/color_tbl_basic.txt $(PNGTOBSPAL)
+pkg/data/legal64.pal: gfx/menufx/loading/legal-64.png tools/png2bestpal/res/color_tbl_basic.txt $(PNGTOBSPAL)
+pkg/data/legal-720p-wide.pal: gfx/menufx/loading/legal-720p-wide.png tools/png2bestpal/res/color_tbl_basic.txt $(PNGTOBSPAL)
+pkg/data/legal-1080p-wide.pal: gfx/menufx/loading/legal-1080p-wide.png tools/png2bestpal/res/color_tbl_basic.txt $(PNGTOBSPAL)
+pkg/data/startfx32.pal: gfx/menufx/loading/startupfx-32.png tools/png2bestpal/res/color_tbl_basic.txt $(PNGTOBSPAL)
+pkg/data/startfx64.pal: gfx/menufx/loading/startupfx-64.png tools/png2bestpal/res/color_tbl_basic.txt $(PNGTOBSPAL)
+pkg/data/loading32.pal: gfx/menufx/loading/loading-32.png tools/png2bestpal/res/color_tbl_basic.txt $(PNGTOBSPAL)
+pkg/data/loading64.pal: gfx/menufx/loading/loading-64.png tools/png2bestpal/res/color_tbl_basic.txt $(PNGTOBSPAL)
+pkg/data/nocd.pal: gfx/menufx/loading/nocd-32.png tools/png2bestpal/res/color_tbl_basic.txt $(PNGTOBSPAL)
+
+pkg/ldata/torture.pal pkg/data/legal32.pal pkg/data/legal64.pal pkg/data/legal-720p-wide.pal pkg/data/legal-1080p-wide.pal pkg/data/startfx32.pal pkg/data/startfx64.pal pkg/data/loading32.pal pkg/data/loading64.pal pkg/data/nocd.pal:
+ -$(ECHO) 'Building palette: $@'
+ @$(MKDIR) $(@D)
+ $(PNGTOBSPAL) -o "$@" -m "$(filter %.txt,$^)" $(filter %.png,$^)
+ -$(ECHO) 'Finished building: $@'
+ -$(ECHO) ' '
+
+pkg/ldata/front.pal: gfx/palettes/front.pal
+pkg/data/palette.dat: gfx/palettes/engine.pal
+
+pkg/ldata/front.pal pkg/data/palette.dat:
+ -$(ECHO) 'Building palette: $@'
+ @$(MKDIR) $(@D)
+ # Simplified, for now
+ $(CP) "$<" "$@"
+ -$(ECHO) 'Finished building: $@'
+ -$(ECHO) ' '
+
+# mark palette files precious to make sure they're not auto-removed after dependencies are built
+.PRECIOUS: pkg/ldata/torture.pal pkg/ldata/front.pal pkg/data/palette.dat
+
+pkg/ldata/lndflag_ens.dat: gfx/landviewdattabs/landview_ensign/filelist_lndflag.txt pkg/campgns/keeporig_lnd/rgmap00.pal $(PNGTORAW)
+pkg/ldata/netflag_ens.dat: gfx/landviewdattabs/landview_ensign/filelist_netflag.txt pkg/campgns/keeporig_lnd/rgmap00.pal $(PNGTORAW)
+pkg/ldata/lndflag_pin.dat: gfx/landviewdattabs/landview_pinpnt/filelist_lndflag.txt pkg/campgns/keeporig_lnd/rgmap00.pal $(PNGTORAW)
+pkg/ldata/netflag_pin.dat: gfx/landviewdattabs/landview_pinpnt/filelist_netflag.txt pkg/campgns/keeporig_lnd/rgmap00.pal $(PNGTORAW)
+pkg/ldata/maphand.dat: gfx/landviewdattabs/landview_hand/filelist_maphand.txt pkg/campgns/keeporig_lnd/rgmap00.pal $(PNGTORAW)
+pkg/ldata/netfont.dat: gfx/menufx/font_net/filelist_netfont.txt pkg/campgns/keeporig_lnd/rgmap00.pal $(PNGTORAW)
+
+pkg/ldata/fronttor.dat: gfx/menufx/torturescr/filelist_fronttor.txt pkg/ldata/torture.pal $(PNGTORAW)
+pkg/ldata/door01.dat: gfx/menufx/torturescr/filelist_tortr_doora.txt pkg/ldata/torture.pal $(PNGTORAW)
+pkg/ldata/door02.dat: gfx/menufx/torturescr/filelist_tortr_doorb.txt pkg/ldata/torture.pal $(PNGTORAW)
+pkg/ldata/door03.dat: gfx/menufx/torturescr/filelist_tortr_doorc.txt pkg/ldata/torture.pal $(PNGTORAW)
+pkg/ldata/door04.dat: gfx/menufx/torturescr/filelist_tortr_doord.txt pkg/ldata/torture.pal $(PNGTORAW)
+pkg/ldata/door05.dat: gfx/menufx/torturescr/filelist_tortr_doore.txt pkg/ldata/torture.pal $(PNGTORAW)
+pkg/ldata/door06.dat: gfx/menufx/torturescr/filelist_tortr_doorf.txt pkg/ldata/torture.pal $(PNGTORAW)
+pkg/ldata/door07.dat: gfx/menufx/torturescr/filelist_tortr_doorg.txt pkg/ldata/torture.pal $(PNGTORAW)
+pkg/ldata/door08.dat: gfx/menufx/torturescr/filelist_tortr_doorh.txt pkg/ldata/torture.pal $(PNGTORAW)
+pkg/ldata/door09.dat: gfx/menufx/torturescr/filelist_tortr_doori.txt pkg/ldata/torture.pal $(PNGTORAW)
+pkg/ldata/torture.raw: gfx/menufx/torturescr/tortr_background.png pkg/ldata/torture.pal $(PNGTORAW)
+
+pkg/ldata/front.raw: gfx/menufx/frontend-64/front_background-64.png pkg/ldata/front.pal $(PNGTORAW)
+pkg/ldata/frontbit.dat: gfx/menufx/frontend-64/filelist_frontbit.txt pkg/ldata/front.pal $(PNGTORAW)
+pkg/ldata/frontft1.dat: gfx/menufx/font_front_hdr_red-64/filelist_frontft1.txt pkg/ldata/front.pal $(PNGTORAW)
+pkg/ldata/frontft2.dat: gfx/menufx/font_front_std_red-64/filelist_frontft2.txt pkg/ldata/front.pal $(PNGTORAW)
+pkg/ldata/frontft3.dat: gfx/menufx/font_front_std_ylw-64/filelist_frontft3.txt pkg/ldata/front.pal $(PNGTORAW)
+pkg/ldata/frontft4.dat: gfx/menufx/font_front_std_dkr-64/filelist_frontft4.txt pkg/ldata/front.pal $(PNGTORAW)
+
+pkg/data/frac00.raw: gfx/enginefx/textures-32/frac00.png gfx/enginefx/textures-32/fract_bw.pal $(PNGTORAW)
+pkg/data/frac01.raw: gfx/enginefx/textures-32/frac01.png gfx/enginefx/textures-32/fract_bw.pal $(PNGTORAW)
+pkg/data/frac02.raw: gfx/enginefx/textures-32/frac02.png gfx/enginefx/textures-32/fract_bw.pal $(PNGTORAW)
+pkg/data/frac03.raw: gfx/enginefx/textures-32/frac03.png gfx/enginefx/textures-32/fract_bw.pal $(PNGTORAW)
+pkg/data/frac04.raw: gfx/enginefx/textures-32/frac04.png gfx/enginefx/textures-32/fract_bw.pal $(PNGTORAW)
+pkg/data/frac05.raw: gfx/enginefx/textures-32/frac05.png gfx/enginefx/textures-32/fract_bw.pal $(PNGTORAW)
+pkg/data/frac06.raw: gfx/enginefx/textures-32/frac06.png gfx/enginefx/textures-32/fract_bw.pal $(PNGTORAW)
+pkg/data/frac07.raw: gfx/enginefx/textures-32/frac07.png gfx/enginefx/textures-32/fract_bw.pal $(PNGTORAW)
+pkg/data/frac08.raw: gfx/enginefx/textures-32/frac08.png gfx/enginefx/textures-32/fract_bw.pal $(PNGTORAW)
+
+pkg/data/tmapa000.dat: gfx/enginefx/textures-32/filelist_tmapa000.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa001.dat: gfx/enginefx/textures-32/filelist_tmapa001.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa002.dat: gfx/enginefx/textures-32/filelist_tmapa002.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa003.dat: gfx/enginefx/textures-32/filelist_tmapa003.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa004.dat: gfx/enginefx/textures-32/filelist_tmapa004.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa005.dat: gfx/enginefx/textures-32/filelist_tmapa005.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa006.dat: gfx/enginefx/textures-32/filelist_tmapa006.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa007.dat: gfx/enginefx/textures-32/filelist_tmapa007.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa008.dat: gfx/enginefx/textures-32/filelist_tmapa008.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa009.dat: gfx/enginefx/textures-32/filelist_tmapa009.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa010.dat: gfx/enginefx/textures-32/filelist_tmapa010.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa011.dat: gfx/enginefx/textures-32/filelist_tmapa011.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa012.dat: gfx/enginefx/textures-32/filelist_tmapa012.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapa013.dat: gfx/enginefx/textures-32/filelist_tmapa013.txt pkg/data/palette.dat $(PNGTORAW)
+
+pkg/data/tmapb000.dat: gfx/enginefx/textures-32/filelist_tmapb000.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb001.dat: gfx/enginefx/textures-32/filelist_tmapb001.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb002.dat: gfx/enginefx/textures-32/filelist_tmapb002.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb003.dat: gfx/enginefx/textures-32/filelist_tmapb003.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb004.dat: gfx/enginefx/textures-32/filelist_tmapb004.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb005.dat: gfx/enginefx/textures-32/filelist_tmapb005.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb006.dat: gfx/enginefx/textures-32/filelist_tmapb006.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb007.dat: gfx/enginefx/textures-32/filelist_tmapb007.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb008.dat: gfx/enginefx/textures-32/filelist_tmapb008.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb009.dat: gfx/enginefx/textures-32/filelist_tmapb009.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb010.dat: gfx/enginefx/textures-32/filelist_tmapb010.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb011.dat: gfx/enginefx/textures-32/filelist_tmapb011.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb012.dat: gfx/enginefx/textures-32/filelist_tmapb012.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/tmapb013.dat: gfx/enginefx/textures-32/filelist_tmapb013.txt pkg/data/palette.dat $(PNGTORAW)
+
+pkg/data/gmap32.raw: gfx/enginefx/guimap/gmap-32.png pkg/data/palette.dat $(PNGTORAW)
+pkg/data/gmap64.raw: gfx/enginefx/guimap/gmap-64.png pkg/data/palette.dat $(PNGTORAW)
+pkg/data/gmapbug.dat: gfx/enginefx/parchmentbug/filelist-gbug.txt pkg/data/palette.dat $(PNGTORAW)
+
+pkg/data/legal32.raw: gfx/menufx/loading/legal-32.png pkg/data/legal32.pal $(PNGTORAW)
+pkg/data/legal64.raw: gfx/menufx/loading/legal-64.png pkg/data/legal64.pal $(PNGTORAW)
+pkg/data/legal-720p-wide.raw: gfx/menufx/loading/legal-720p-wide.png pkg/data/legal-720p-wide.pal $(PNGTORAW)
+pkg/data/legal-1080p-wide.raw: gfx/menufx/loading/legal-1080p-wide.png pkg/data/legal-1080p-wide.pal $(PNGTORAW)
+pkg/data/startfx32.raw: gfx/menufx/loading/startupfx-32.png pkg/data/startfx32.pal $(PNGTORAW)
+pkg/data/startfx64.raw: gfx/menufx/loading/startupfx-64.png pkg/data/startfx64.pal $(PNGTORAW)
+pkg/data/loading32.raw: gfx/menufx/loading/loading-32.png pkg/data/loading32.pal $(PNGTORAW)
+pkg/data/loading64.raw: gfx/menufx/loading/loading-64.png pkg/data/loading64.pal $(PNGTORAW)
+pkg/data/nocd.raw: gfx/menufx/loading/nocd-32.png pkg/data/nocd.pal $(PNGTORAW)
+pkg/data/gui2-64.dat: gfx/menufx/gui2-64/filelist_gui2.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/gui2-32.dat: gfx/menufx/gui2-32/filelist_gui2.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/gui1-64.dat: gfx/menufx/gui1-64/filelist_gui1.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/gui1-32.dat: gfx/menufx/gui1-32/filelist_gui1.txt pkg/data/palette.dat $(PNGTORAW)
+
+pkg/data/pointer64.dat: gfx/enginefx/pointer-64/filelist_pointer.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/font1-64.dat: gfx/enginefx/font_simp-64/filelist_font1.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/font1-32.dat: gfx/enginefx/font_simp-32/filelist_font1.txt pkg/data/palette.dat $(PNGTORAW)
+
+pkg/data/font2-32.dat: gfx/menufx/font2-32/filelist_font2.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/font2-64.dat: gfx/menufx/font2-64/filelist_font2.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/creature.jty: gfx/enginefx/sprites-32/animlist.txt pkg/data/palette.dat $(PNGTORAW)
+
+pkg/data/swipe01.dat: gfx/enginefx/swipes-32/filelist_bhandrl.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/swipe02.dat: gfx/enginefx/swipes-32/filelist_swordrl.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/swipe03.dat: gfx/enginefx/swipes-32/filelist_scythlr.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/swipe04.dat: gfx/enginefx/swipes-32/filelist_sticklr.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/swipe05.dat: gfx/enginefx/swipes-32/filelist_stickrl.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/swipe06.dat: gfx/enginefx/swipes-32/filelist_clawsrl.txt pkg/data/palette.dat $(PNGTORAW)
+pkg/data/swipe07.dat: gfx/enginefx/swipes-32/filelist_teeth.txt pkg/data/palette.dat $(PNGTORAW)
+
+pkg/data/frac%.raw:
+ -$(ECHO) 'Building RAW texture: $@'
+ $(PNGTORAW) -o "$@" -p "$(word 2,$^)" -r 255 -f raw -l 100 "$<"
+ -$(ECHO) 'Finished building: $@'
+ -$(ECHO) ' '
+
+pkg/data/tmap%.dat:
+ -$(ECHO) 'Building RAW texture: $@'
+ $(PNGTORAW) -b -o "$@" -p "$(word 2,$^)" -f raw -l 0 "$<"
+ -$(ECHO) 'Finished building: $@'
+ -$(ECHO) ' '
+
+define BUILD_RAW_IMAGE_CMD
+ -$(ECHO) 'Building RAW image: $@'
+ $(PNGTORAW) -o "$@" -p "$(word 2,$^)" -f raw -l 100 "$<"
+ -$(ECHO) 'Finished building: $@'
+ -$(ECHO) ' '
+endef
+
+pkg/ldata/%.raw:
+ $(BUILD_RAW_IMAGE_CMD)
+
+pkg/data/%.raw:
+ $(BUILD_RAW_IMAGE_CMD)
+
+
+define BUILD_TABULATED_SPRITES_CMD
+ -$(ECHO) 'Building tabulated sprites: $@'
+ $(MKDIR) "$(@D)"
+ $(PNGTORAW) -b -o "$@" -p "$(word 2,$^)" -f sspr -l 0 "$<"
+ -$(ECHO) 'Finished building: $@'
+ -$(ECHO) ' '
+endef
+
+pkg/ldata/%.dat:
+ $(BUILD_TABULATED_SPRITES_CMD)
+
+pkg/data/%.dat:
+ $(BUILD_TABULATED_SPRITES_CMD)
+
+
+define BUILD_JONTY_SPRITES_CMD
+ -$(ECHO) 'Building jonty sprites: $@'
+ @$(MKDIR) "$(@D)"
+ $(PNGTORAW) -m -o "$@" -p "$(word 2,$^)" -f jspr -l 0 "$<"
+ -$(ECHO) 'Finished building: $@'
+ -$(ECHO) ' '
+endef
+
+pkg/creatrs/%.jty:
+ $(BUILD_JONTY_SPRITES_CMD)
+
+pkg/data/%.jty:
+ $(BUILD_JONTY_SPRITES_CMD)
+
+
+# ============================================================================
+# MODULAR ASSET ARCHITECTURE: Submodule-based gfx/ directory
+# ============================================================================
+# The gfx/ directory is now a git submodule, not an ephemeral clone.
+# To initialize: git submodule update --init --recursive
+# Original clone rule commented out below:
+#
+# gfx/%:: | gfx/LICENSE ;
+# gfx/LICENSE:
+# git clone --depth=1 https://github.com/dkfans/FXGraphics.git gfx
+
+# Submodule existence check
+gfx/%:: | gfx/.git ;
+
+gfx/.git:
+ @echo "ERROR: gfx/ submodule not initialized"
+ @echo "Run: git submodule update --init --recursive"
+ @exit 1
+
+# The package is extracted only if targets does not exits; the "|" causes file dates to be ignored
+# Note that ignoring timestamp means it is possible to have outadated files after a new
+# package release, if no targets were modified with the update.
+$(foreach campaign,$(sort $(CAMPAIGNS)), gfx/$(campaign)_lnd/%.png) \
+gfx/menufx/loading/%.png gfx/palettes/%.pal \
+gfx/menufx/font2-32/%.txt gfx/menufx/font2-64/%.txt gfx/menufx/font_net/%.txt \
+gfx/menufx/font_front_std_dkr-64/%.txt gfx/menufx/font_front_std_red-64/%.txt gfx/menufx/font_front_std_ylw-64/%.txt \
+gfx/menufx/font_front_hdr_dkr-64/%.txt gfx/menufx/font_front_hdr_red-64/%.txt gfx/menufx/font_front_hdr_ylw-64/%.txt \
+gfx/menufx/frontend-64/%.txt gfx/enginefx/pointer-64/%.txt \
+gfx/menufx/gui1-32/%.txt gfx/menufx/gui1-64/%.txt gfx/menufx/gui1-128/%.txt gfx/menufx/gui1-256/%.txt \
+gfx/menufx/gui2-32/%.txt gfx/menufx/gui2-64/%.txt gfx/menufx/gui2-128/%.txt gfx/menufx/gui2-256/%.txt \
+gfx/landviewdattabs/landview_ensign/%.txt gfx/landviewdattabs/landview_hand/%.txt gfx/landviewdattabs/landview_pinpnt/%.txt \
+gfx/enginefx/font_simp-32/%.txt gfx/enginefx/font_simp-64/%.txt \
+gfx/enginefx/sprites-32/%.txt gfx/enginefx/sprites-64/%.txt gfx/enginefx/sprites-128/%.txt \
+gfx/enginefx/swipes-32/%.txt gfx/enginefx/swipes-64/%.txt gfx/enginefx/swipes-128/%.txt \
+gfx/enginefx/textures-32/%.png gfx/enginefx/textures-64/%.png gfx/enginefx/textures-128/%.png \
+gfx/enginefx/textures-32/%.txt gfx/enginefx/textures-64/%.txt gfx/enginefx/textures-128/%.txt \
+gfx/menufx/torturescr/%.png gfx/menufx/torturescr/%.txt gfx/landviewdattabs/guimap/%.txt gfx/enginefx/parchmentbug/%.txt gfx/creatrportrait/%.txt: gfx
+
+#******************************************************************************
diff --git a/build/make/pkg_lang.mk b/build/make/pkg_lang.mk
new file mode 100644
index 0000000000..637f231a4b
--- /dev/null
+++ b/build/make/pkg_lang.mk
@@ -0,0 +1,232 @@
+#******************************************************************************
+# Free implementation of Bullfrog's Dungeon Keeper strategy game.
+#******************************************************************************
+# @file tool_peresec.mk
+# A script used by GNU Make to recompile the project.
+# @par Purpose:
+# Defines make rules for tools needed to build KeeperFX.
+# Most tools can either by compiled from source or downloaded.
+# @par Comment:
+# None.
+# @author Tomasz Lis
+# @date 25 Jan 2009 - 02 Jul 2011
+# @par Copying and copyrights:
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+#******************************************************************************
+LANGUAGE ?= eng
+
+NGTEXTDATS = \
+pkg/fxdata/gtext_eng.dat \
+pkg/fxdata/gtext_chi.dat \
+pkg/fxdata/gtext_cht.dat \
+pkg/fxdata/gtext_cze.dat \
+pkg/fxdata/gtext_dut.dat \
+pkg/fxdata/gtext_fre.dat \
+pkg/fxdata/gtext_ger.dat \
+pkg/fxdata/gtext_ita.dat \
+pkg/fxdata/gtext_jpn.dat \
+pkg/fxdata/gtext_kor.dat \
+pkg/fxdata/gtext_lat.dat \
+pkg/fxdata/gtext_pol.dat \
+pkg/fxdata/gtext_rus.dat \
+pkg/fxdata/gtext_spa.dat \
+pkg/fxdata/gtext_swe.dat \
+pkg/fxdata/gtext_ukr.dat \
+pkg/fxdata/gtext_por.dat \
+
+NCTEXTDATS = \
+pkg/campgns/ami2019/text_eng.dat \
+pkg/campgns/ami2019/text_chi.dat \
+pkg/campgns/ami2019/text_ger.dat \
+pkg/campgns/ami2019/text_spa.dat \
+pkg/campgns/ancntkpr/text_eng.dat \
+pkg/campgns/ancntkpr/text_chi.dat \
+pkg/campgns/ancntkpr/text_fre.dat \
+pkg/campgns/ancntkpr/text_ger.dat \
+pkg/campgns/ancntkpr/text_pol.dat \
+pkg/campgns/ancntkpr/text_por.dat \
+pkg/campgns/ancntkpr/text_spa.dat \
+pkg/campgns/ancntkpr/text_lat.dat \
+pkg/campgns/burdnimp/text_eng.dat \
+pkg/campgns/burdnimp/text_chi.dat \
+pkg/campgns/burdnimp/text_pol.dat \
+pkg/campgns/burdnimp/text_por.dat \
+pkg/campgns/burdnimp/text_spa.dat \
+pkg/campgns/lqizgood/text_eng.dat \
+pkg/campgns/lqizgood/text_chi.dat \
+pkg/campgns/lqizgood/text_fre.dat \
+pkg/campgns/lqizgood/text_pol.dat \
+pkg/campgns/lqizgood/text_por.dat \
+pkg/campgns/lqizgood/text_ukr.dat \
+pkg/campgns/origplus/text_eng.dat \
+pkg/campgns/origplus/text_chi.dat \
+pkg/campgns/origplus/text_cht.dat \
+pkg/campgns/origplus/text_cze.dat \
+pkg/campgns/origplus/text_dut.dat \
+pkg/campgns/origplus/text_fre.dat \
+pkg/campgns/origplus/text_ger.dat \
+pkg/campgns/origplus/text_ita.dat \
+pkg/campgns/origplus/text_jpn.dat \
+pkg/campgns/origplus/text_kor.dat \
+pkg/campgns/origplus/text_lat.dat \
+pkg/campgns/origplus/text_pol.dat \
+pkg/campgns/origplus/text_por.dat \
+pkg/campgns/origplus/text_rus.dat \
+pkg/campgns/origplus/text_spa.dat \
+pkg/campgns/origplus/text_swe.dat \
+pkg/campgns/origplus/text_ukr.dat \
+pkg/campgns/revlord/text_eng.dat \
+pkg/campgns/revlord/text_chi.dat \
+pkg/campgns/revlord/text_ger.dat \
+pkg/campgns/revlord/text_por.dat \
+pkg/campgns/revlord/text_spa.dat \
+pkg/campgns/twinkprs/text_eng.dat \
+pkg/campgns/twinkprs/text_chi.dat \
+pkg/campgns/twinkprs/text_fre.dat \
+pkg/campgns/twinkprs/text_jpn.dat \
+pkg/campgns/twinkprs/text_pol.dat \
+pkg/campgns/twinkprs/text_por.dat \
+pkg/campgns/twinkprs/text_spa.dat \
+pkg/campgns/twinkprs/text_lat.dat \
+pkg/campgns/undedkpr/text_eng.dat \
+pkg/campgns/undedkpr/text_chi.dat \
+pkg/campgns/undedkpr/text_ger.dat \
+pkg/campgns/undedkpr/text_pol.dat \
+pkg/campgns/undedkpr/text_por.dat \
+pkg/campgns/undedkpr/text_spa.dat
+
+MPTEXTDATS = \
+pkg/levels/classic/text_eng.dat \
+pkg/levels/classic/text_chi.dat \
+pkg/levels/classic/text_fre.dat \
+pkg/levels/classic/text_ger.dat \
+pkg/levels/classic/text_por.dat \
+pkg/levels/classic/text_spa.dat \
+pkg/levels/classic/text_rus.dat \
+pkg/levels/standard/text_eng.dat \
+pkg/levels/standard/text_chi.dat \
+pkg/levels/standard/text_fre.dat \
+pkg/levels/standard/text_ger.dat \
+pkg/levels/standard/text_por.dat \
+pkg/levels/standard/text_spa.dat \
+pkg/levels/lostlvls/text_eng.dat \
+pkg/levels/lostlvls/text_por.dat \
+pkg/levels/lostlvls/text_chi.dat \
+pkg/levels/lostlvls/text_rus.dat
+
+EU_CHAR_ENCODING = tools/po2ngdat/res/char_encoding_tbl_eu.txt
+JP_CHAR_ENCODING = tools/po2ngdat/res/char_encoding_tbl_jp.txt
+RU_CHAR_ENCODING = tools/po2ngdat/res/char_encoding_tbl_ru.txt
+CH_CHAR_ENCODING = tools/po2ngdat/res/char_encoding_tbl_ch.txt
+KR_CHAR_ENCODING = tools/po2ngdat/res/char_encoding_tbl_kr.txt
+
+.PRECIOUS: $(EU_CHAR_ENCODING) $(JP_CHAR_ENCODING) $(RU_CHAR_ENCODING) $(CH_CHAR_ENCODING) $(KR_CHAR_ENCODING)
+
+pkg-languages: $(NGTEXTDATS) $(NCTEXTDATS) $(MPTEXTDATS)
+
+# Creation of Only single language engine language files from PO/POT files (for development)
+pkg-lang-single: pkg/fxdata/gtext_$(LANGUAGE).dat
+
+$(patsubst %/,%,$(sort $(dir $(NCTEXTDATS)))):
+ $(MKDIR) $@
+
+$(patsubst %/,%,$(sort $(dir $(MPTEXTDATS)))):
+ $(MKDIR) $@
+
+# Creation of engine language files from PO/POT files
+pkg/fxdata/gtext_jpn.dat: lang/gtext_jpn.po $(POTONGDAT) $(JP_CHAR_ENCODING) | pkg/fxdata
+ $(POTONGDAT) -o $@ -e $(JP_CHAR_ENCODING) $< >/dev/null
+
+pkg/fxdata/gtext_rus.dat: lang/gtext_rus.po $(POTONGDAT) $(RU_CHAR_ENCODING) | pkg/fxdata
+ $(POTONGDAT) -o $@ -e $(RU_CHAR_ENCODING) $< >/dev/null
+
+pkg/fxdata/gtext_ukr.dat: lang/gtext_ukr.po $(POTONGDAT) $(RU_CHAR_ENCODING) | pkg/fxdata
+ $(POTONGDAT) -o $@ -e $(RU_CHAR_ENCODING) $< >/dev/null
+
+pkg/fxdata/gtext_chi.dat: lang/gtext_chi.po $(POTONGDAT) $(CH_CHAR_ENCODING) | pkg/fxdata
+ $(POTONGDAT) -o $@ -e $(CH_CHAR_ENCODING) $< >/dev/null
+
+pkg/fxdata/gtext_cht.dat: lang/gtext_cht.po $(POTONGDAT) $(CH_CHAR_ENCODING) | pkg/fxdata
+ $(POTONGDAT) -o $@ -e $(CH_CHAR_ENCODING) $< >/dev/null
+
+pkg/fxdata/gtext_kor.dat: lang/gtext_kor.po $(POTONGDAT) $(KR_CHAR_ENCODING) | pkg/fxdata
+ $(POTONGDAT) -o $@ -e $(KR_CHAR_ENCODING) $< >/dev/null
+
+pkg/fxdata/gtext_%.dat: lang/gtext_%.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/fxdata
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/fxdata/gtext_%.dat: lang/gtext_%.pot $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/fxdata
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_chi.dat : lang/%/text_chi.po $(POTONGDAT) $(CH_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(CH_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_cht.dat : lang/%/text_cht.po $(POTONGDAT) $(CH_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(CH_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_cze.dat: lang/%/text_cze.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_dut.dat: lang/%/text_dut.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_fre.dat: lang/%/text_fre.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_ger.dat: lang/%/text_ger.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_ita.dat: lang/%/text_ita.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_kor.dat: lang/%/text_kor.po $(POTONGDAT) $(KR_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(KR_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_jpn.dat: lang/%/text_jpn.po $(POTONGDAT) $(JP_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(JP_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_lat.dat: lang/%/text_lat.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_pol.dat: lang/%/text_pol.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_por.dat: lang/%/text_por.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_rus.dat: lang/%/text_rus.po $(POTONGDAT) $(RU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(RU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_ukr.dat: lang/%/text_ukr.po $(POTONGDAT) $(RU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(RU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_spa.dat: lang/%/text_spa.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_swe.dat: lang/%/text_swe.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/landview_pol.dat: lang/%/landview_pol.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/landview_dut.dat: lang/%/landview_dut.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/landview_fre.dat: lang/%/landview_fre.po $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/landview_kor.dat: lang/%/landview_kor.po $(POTONGDAT) $(KR_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(KR_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/landview_rus.dat: lang/%/landview_rus.po $(POTONGDAT) $(RU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(RU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/text_eng.dat: lang/%/text_eng.pot $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
+
+pkg/%/landview_eng.dat: lang/%/landview_eng.pot $(POTONGDAT) $(EU_CHAR_ENCODING) | pkg/%
+ $(POTONGDAT) -o $@ -e $(EU_CHAR_ENCODING) $< >/dev/null
diff --git a/build/make/pkg_sfx.mk b/build/make/pkg_sfx.mk
new file mode 100644
index 0000000000..f20a45d7d5
--- /dev/null
+++ b/build/make/pkg_sfx.mk
@@ -0,0 +1,130 @@
+#******************************************************************************
+# Free implementation of Bullfrog's Dungeon Keeper strategy game.
+#******************************************************************************
+# @file pkg_sfx.mk
+# A script used by GNU Make to recompile the project.
+# @par Purpose:
+# Defines make rules for tools needed to build KeeperFX.
+# Most tools can either by compiled from source or downloaded.
+# @par Comment:
+# None.
+# @author Tomasz Lis
+# @date 25 Jan 2009 - 02 Jul 2011
+# @par Copying and copyrights:
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+#******************************************************************************
+
+NGSPEECHBANKS = \
+speech_chi \
+speech_cht \
+speech_dut \
+speech_eng \
+speech_fre \
+speech_ger \
+speech_ita \
+speech_jpn \
+speech_kor \
+speech_lat \
+speech_pol \
+speech_rus \
+speech_spa \
+speech_swe
+
+NGSOUNDDATS = $(patsubst %,pkg/sound/%.dat,$(NGSPEECHBANKS) sound)
+
+NGSOUNDLISTS = $(patsubst %,sfx/%/filelist.txt,$(NGSPEECHBANKS) sound)
+
+LANDVIEWSPEECH = \
+$(foreach lng,eng chi,ancntkpr_$(lng)) \
+$(foreach lng,eng chi,burdnimp_$(lng)) \
+$(foreach lng,eng chi dut,dzjr06lv_$(lng)) \
+$(foreach lng,eng chi,jdkmaps8_$(lng)) \
+$(foreach lng,eng chi cht dut fre ger ita jpn kor pol rus spa swe,keeporig_$(lng)) \
+$(foreach lng,eng chi dut,lqizgood_$(lng)) \
+$(foreach lng,eng chi,postanck_$(lng)) \
+$(foreach lng,eng chi,pstunded_$(lng)) \
+$(foreach lng,eng chi,revlord_$(lng)) \
+$(foreach lng,eng dut,twinkprs_$(lng)) \
+$(foreach lng,eng chi,undedkpr_$(lng))
+
+LANDVIEWSPEECHDIRS = $(patsubst %,pkg/campgns/%,$(LANDVIEWSPEECH))
+
+.PHONY: pkg-sfx convert-sfx
+
+pkg-sfx: $(NGSOUNDDATS) $(LANDVIEWSPEECHDIRS)
+
+pkg/sound/sound.dat: sfx/sound/filelist.txt $(WAVTODAT)
+pkg/sound/speech_chi.dat: sfx/speech_chi/filelist.txt $(WAVTODAT)
+pkg/sound/speech_cht.dat: sfx/speech_cht/filelist.txt $(WAVTODAT)
+pkg/sound/speech_dut.dat: sfx/speech_dut/filelist.txt $(WAVTODAT)
+pkg/sound/speech_eng.dat: sfx/speech_eng/filelist.txt $(WAVTODAT)
+pkg/sound/speech_fre.dat: sfx/speech_fre/filelist.txt $(WAVTODAT)
+pkg/sound/speech_ger.dat: sfx/speech_ger/filelist.txt $(WAVTODAT)
+pkg/sound/speech_ita.dat: sfx/speech_ita/filelist.txt $(WAVTODAT)
+pkg/sound/speech_jpn.dat: sfx/speech_jpn/filelist.txt $(WAVTODAT)
+pkg/sound/speech_kor.dat: sfx/speech_kor/filelist.txt $(WAVTODAT)
+pkg/sound/speech_lat.dat: sfx/speech_lat/filelist.txt $(WAVTODAT)
+pkg/sound/speech_pol.dat: sfx/speech_pol/filelist.txt $(WAVTODAT)
+pkg/sound/speech_rus.dat: sfx/speech_rus/filelist.txt $(WAVTODAT)
+pkg/sound/speech_spa.dat: sfx/speech_spa/filelist.txt $(WAVTODAT)
+pkg/sound/speech_swe.dat: sfx/speech_swe/filelist.txt $(WAVTODAT)
+
+pkg/sound/%.dat:
+ -$(ECHO) 'Building sound bank: $@'
+ @$(MKDIR) "$(@D)"
+ $(WAVTODAT) -o "$@" "$<"
+ -$(ECHO) 'Finished building: $@'
+ -$(ECHO) ' '
+
+# Creation of land view speeches for campaigns
+define define_campaign_speeches_rule
+pkg/campgns/$(1)_$(2): sfx/campgns/$(1)_$(2)/filelist.txt
+ -$(ECHO) 'Copying campaign SFX: $$@'
+ @$(MKDIR) "$$@"
+ tail -n +2 "$$<" | cut -f1 | xargs -d '\n' -I {} $(CP) "$$(/dev/null
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-dkillconv:
+ -$(RM) tools/dkillconv/bin/*
+
+deep-clean-dkillconv:
+ -$(RM) tools/dkillconv/pkg/$(DKILLCONV_PACKAGE)
+
+else ifneq (,$(findstring .zip,$(DKILLCONV_PACKAGE)))
+
+# If we have zip prebuild, download and extract it
+$(DKILLTOLVL): tools/dkillconv/pkg/$(DKILLCONV_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ unzip -DD -qo "../../../$<"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/dkillconv/pkg/$(DKILLCONV_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(DKILLCONV_DOWNLOAD)"
+ unzip -qt "$@.dl"
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-dkillconv:
+ -$(RM) tools/dkillconv/bin/*
+
+deep-clean-dkillconv:
+ -$(RM) tools/dkillconv/pkg/$(DKILLCONV_PACKAGE)
+
+else
+
+$(error Cannot find dkillconv tool source nor prebuild. Get package or source manually.)
+
+endif
+
+#******************************************************************************
diff --git a/build/make/tool_png2bestpal.mk b/build/make/tool_png2bestpal.mk
new file mode 100644
index 0000000000..0d5776ebe0
--- /dev/null
+++ b/build/make/tool_png2bestpal.mk
@@ -0,0 +1,112 @@
+#******************************************************************************
+# Free implementation of Bullfrog's Dungeon Keeper strategy game.
+#******************************************************************************
+# @file tool_png2bestpal.mk
+# A script used by GNU Make to recompile the project.
+# @par Purpose:
+# Defines make rules for tools needed to build KeeperFX.
+# Most tools can either by compiled from source or downloaded.
+# @par Comment:
+# None.
+# @author Tomasz Lis
+# @date 25 Jan 2009 - 02 Jul 2011
+# @par Copying and copyrights:
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+#******************************************************************************
+
+.PHONY: clean-png2bestpal deep-clean-png2bestpal
+
+tools: $(PNGTOBSPAL)
+
+clean-tools: clean-png2bestpal
+
+deep-clean-tools: deep-clean-png2bestpal
+
+ifneq (,$(wildcard tools/png2bestpal/src/png2bestpal.cpp))
+
+# If we have source code of this tool, compile it
+$(PNGTOBSPAL): tools/png2bestpal/src/png2bestpal.cpp
+ $(MAKE) -C tools/png2bestpal
+
+clean-png2bestpal:
+ $(MAKE) -C tools/png2bestpal clean
+
+else ifneq (,$(findstring .tar.gz,$(PNGTOBSPAL_PACKAGE)))
+
+# If we have tar gzip prebuild, download and extract it
+$(PNGTOBSPAL): tools/png2bestpal/pkg/$(PNGTOBSPAL_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ tar -zxmUf "../../../$<" --exclude="*color_tbl_*.txt"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/png2bestpal/res/%.txt: tools/png2bestpal/pkg/$(PNGTOBSPAL_PACKAGE)
+ -$(ECHO) 'Extracting encoding table: $@'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ tar -zxmUf "../../../$<" --wildcards "*color_tbl_*.txt"
+ -$(ECHO) 'Finished extracting: $@'
+ -$(ECHO) ' '
+
+tools/png2bestpal/pkg/$(PNGTOBSPAL_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(PNGTOBSPAL_DOWNLOAD)"
+ tar -tzf "$@.dl" >/dev/null
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-png2bestpal:
+ -$(RM) tools/png2bestpal/bin/*
+
+deep-clean-png2bestpal:
+ -$(RM) tools/png2bestpal/pkg/$(PNGTOBSPAL_PACKAGE)
+
+else ifneq (,$(findstring .zip,$(PNGTOBSPAL_PACKAGE)))
+
+# If we have zip prebuild, download and extract it
+$(PNGTOBSPAL): tools/png2bestpal/pkg/$(PNGTOBSPAL_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ unzip -DD -qo "../../../$<" -x "color_tbl_*.txt"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/png2bestpal/res/%.txt: tools/png2bestpal/pkg/$(PNGTOBSPAL_PACKAGE)
+ -$(ECHO) 'Extracting encoding table: $@'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ unzip -DD -qo "../../../$<" "color_tbl_*.txt"
+ -$(ECHO) 'Finished extracting: $@'
+ -$(ECHO) ' '
+
+tools/png2bestpal/pkg/$(PNGTOBSPAL_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(PNGTOBSPAL_DOWNLOAD)"
+ unzip -qt "$@.dl"
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-png2bestpal:
+ -$(RM) tools/png2bestpal/bin/*
+
+deep-clean-png2bestpal:
+ -$(RM) tools/png2bestpal/pkg/$(PNGTOBSPAL_PACKAGE)
+
+else
+
+$(error Cannot find png2bestpal tool source nor prebuild. Get package or source manually.)
+
+endif
+
+#******************************************************************************
diff --git a/build/make/tool_png2ico.mk b/build/make/tool_png2ico.mk
new file mode 100644
index 0000000000..9d9c484b77
--- /dev/null
+++ b/build/make/tool_png2ico.mk
@@ -0,0 +1,96 @@
+#******************************************************************************
+# Free implementation of Bullfrog's Dungeon Keeper strategy game.
+#******************************************************************************
+# @file tool_png2ico.mk
+# A script used by GNU Make to recompile the project.
+# @par Purpose:
+# Defines make rules for tools needed to build KeeperFX.
+# Most tools can either by compiled from source or downloaded.
+# @par Comment:
+# None.
+# @author Tomasz Lis
+# @date 25 Jan 2009 - 02 Jul 2011
+# @par Copying and copyrights:
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+#******************************************************************************
+
+.PHONY: clean-png2ico deep-clean-png2ico
+
+tools: $(PNGTOICO)
+
+clean-tools: clean-png2ico
+
+deep-clean-tools: deep-clean-png2ico
+
+ifneq (,$(wildcard tools/png2ico/png2ico.cpp))
+
+# If we have source code of this tool, compile it
+$(PNGTOICO): tools/png2ico/png2ico.cpp
+ $(MAKE) -C tools/png2ico
+
+clean-png2ico:
+ $(MAKE) -C tools/png2ico clean
+
+else ifneq (,$(findstring .tar.gz,$(PNGTOICO_PACKAGE)))
+
+# If we have tar gzip prebuild, download and extract it
+$(PNGTOICO): tools/png2ico/$(PNGTOICO_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ tar -zxmf "../../$<"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/png2ico/$(PNGTOICO_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(PNGTOICO_DOWNLOAD)"
+ tar -tzf "$@.dl" >/dev/null
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-png2ico:
+ -$(RM) $(PNGTOICO) tools/png2ico/README tools/png2ico/VERSION tools/png2ico/LICENSE tools/png2ico/doc/png2ico.txt
+
+deep-clean-png2ico:
+ -$(RM) tools/png2ico/$(PNGTOICO_PACKAGE)
+
+else ifneq (,$(findstring .zip,$(PNGTOICO_PACKAGE)))
+
+# If we have zip prebuild, download and extract it
+$(PNGTOICO): tools/png2ico/$(PNGTOICO_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ unzip -DD -qo "../../$<"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/png2ico/$(PNGTOICO_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(PNGTOICO_DOWNLOAD)"
+ unzip -qt "$@.dl"
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-png2ico:
+ -$(RM) $(PNGTOICO) tools/png2ico/README tools/png2ico/VERSION tools/png2ico/LICENSE tools/png2ico/doc/png2ico.txt
+
+deep-clean-png2ico:
+ -$(RM) tools/png2ico/$(PNGTOICO_PACKAGE)
+
+else
+
+$(error Cannot find png2ico tool source nor prebuild. Get package or source manually.)
+
+endif
+
+#******************************************************************************
diff --git a/build/make/tool_pngpal2raw.mk b/build/make/tool_pngpal2raw.mk
new file mode 100644
index 0000000000..1ad9a07e29
--- /dev/null
+++ b/build/make/tool_pngpal2raw.mk
@@ -0,0 +1,96 @@
+#******************************************************************************
+# Free implementation of Bullfrog's Dungeon Keeper strategy game.
+#******************************************************************************
+# @file tool_pngpal2raw.mk
+# A script used by GNU Make to recompile the project.
+# @par Purpose:
+# Defines make rules for tools needed to build KeeperFX.
+# Most tools can either by compiled from source or downloaded.
+# @par Comment:
+# None.
+# @author Tomasz Lis
+# @date 25 Jan 2009 - 02 Jul 2011
+# @par Copying and copyrights:
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+#******************************************************************************
+
+.PHONY: clean-pngpal2raw deep-clean-pngpal2raw
+
+tools: $(PNGTORAW)
+
+clean-tools: clean-pngpal2raw
+
+deep-clean-tools: deep-clean-pngpal2raw
+
+ifneq (,$(wildcard tools/pngpal2raw/src/pngpal2raw.cpp))
+
+# If we have source code of this tool, compile it
+$(PNGTORAW): tools/pngpal2raw/src/pngpal2raw.cpp
+ $(MAKE) -C tools/pngpal2raw
+
+clean-pngpal2raw:
+ $(MAKE) -C tools/pngpal2raw clean
+
+else ifneq (,$(findstring .tar.gz,$(PNGTORAW_PACKAGE)))
+
+# If we have tar gzip prebuild, download and extract it
+$(PNGTORAW): tools/pngpal2raw/pkg/$(PNGTORAW_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ tar -zxmUf "../../../$<"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/pngpal2raw/pkg/$(PNGTORAW_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(PNGTORAW_DOWNLOAD)"
+ tar -tzf "$@.dl" >/dev/null
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-pngpal2raw:
+ -$(RM) tools/pngpal2raw/bin/*
+
+deep-clean-pngpal2raw:
+ -$(RM) tools/pngpal2raw/pkg/$(PNGTORAW_PACKAGE)
+
+else ifneq (,$(findstring .zip,$(PNGTORAW_PACKAGE)))
+
+# If we have zip prebuild, download and extract it
+$(PNGTORAW): tools/pngpal2raw/pkg/$(PNGTORAW_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ unzip -DD -qo "../../../$<"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/pngpal2raw/pkg/$(PNGTORAW_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(PNGTORAW_DOWNLOAD)"
+ unzip -qt "$@.dl"
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-pngpal2raw:
+ -$(RM) tools/pngpal2raw/bin/*
+
+deep-clean-pngpal2raw:
+ -$(RM) tools/pngpal2raw/pkg/$(PNGTORAW_PACKAGE)
+
+else
+
+$(error Cannot find pngpal2raw tool source nor prebuild. Get package or source manually.)
+
+endif
+
+#******************************************************************************
diff --git a/build/make/tool_po2ngdat.mk b/build/make/tool_po2ngdat.mk
new file mode 100644
index 0000000000..fba890752a
--- /dev/null
+++ b/build/make/tool_po2ngdat.mk
@@ -0,0 +1,110 @@
+#******************************************************************************
+# Free implementation of Bullfrog's Dungeon Keeper strategy game.
+#******************************************************************************
+# @file tool_po2ngdat.mk
+# A script used by GNU Make to recompile the project.
+# @par Purpose:
+# Defines make rules for tools needed to build KeeperFX.
+# Most tools can either by compiled from source or downloaded.
+# @par Comment:
+# None.
+# @author Tomasz Lis
+# @date 25 Jan 2009 - 02 Jul 2011
+# @par Copying and copyrights:
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+#******************************************************************************
+
+.PHONY: clean-po2ngdat deep-clean-po2ngdat
+
+tools: $(POTONGDAT)
+
+clean-tools: clean-po2ngdat
+
+deep-clean-tools: deep-clean-po2ngdat
+
+ifneq (,$(wildcard tools/po2ngdat/src/po2ngdat.cpp))
+
+# If we have source code of this tool, compile it
+$(POTONGDAT): tools/po2ngdat/src/po2ngdat.cpp
+ $(MAKE) -C tools/po2ngdat
+
+clean-po2ngdat:
+ $(MAKE) -C tools/po2ngdat clean
+
+else ifneq (,$(findstring .tar.gz,$(POTONGDAT_PACKAGE)))
+
+# If we have tar gzip prebuild, download and extract it
+$(POTONGDAT): tools/po2ngdat/pkg/$(POTONGDAT_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ tar -zxmUf "../../../$<" --exclude="*char_encoding_*.txt"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/po2ngdat/res:
+ $(MKDIR) $@
+
+tools/po2ngdat/res/char_encoding_tbl_%.txt: tools/po2ngdat/pkg/$(POTONGDAT_PACKAGE) | tools/po2ngdat/res
+ tar xzmf $< -C $(@D) ./$(@F)
+
+tools/po2ngdat/pkg/$(POTONGDAT_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(POTONGDAT_DOWNLOAD)"
+ tar -tzf "$@.dl" >/dev/null
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-po2ngdat:
+ -$(RM) tools/po2ngdat/bin/*
+
+deep-clean-po2ngdat:
+ -$(RM) tools/po2ngdat/pkg/$(POTONGDAT_PACKAGE)
+
+else ifneq (,$(findstring .zip,$(POTONGDAT_PACKAGE)))
+
+# If we have zip prebuild, download and extract it
+$(POTONGDAT): tools/po2ngdat/pkg/$(POTONGDAT_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ unzip -DD -qo "../../../$<" -x "char_encoding_*.txt"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/po2ngdat/res/%.txt: tools/po2ngdat/pkg/$(POTONGDAT_PACKAGE)
+ -$(ECHO) 'Extracting encoding table: $@'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ unzip -DD -qo "../../../$<" "char_encoding_*.txt"
+ -$(ECHO) 'Finished extracting: $@'
+ -$(ECHO) ' '
+
+tools/po2ngdat/pkg/$(POTONGDAT_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(POTONGDAT_DOWNLOAD)"
+ unzip -qt "$@.dl"
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-po2ngdat:
+ -$(RM) tools/po2ngdat/bin/*
+
+deep-clean-po2ngdat:
+ -$(RM) tools/po2ngdat/pkg/$(POTONGDAT_PACKAGE)
+
+else
+
+$(error Cannot find po2ngdat tool source nor prebuild. Get package or source manually.)
+
+endif
+
+#******************************************************************************
diff --git a/build/make/tool_rnctools.mk b/build/make/tool_rnctools.mk
new file mode 100644
index 0000000000..2e3ef8bc8c
--- /dev/null
+++ b/build/make/tool_rnctools.mk
@@ -0,0 +1,96 @@
+#******************************************************************************
+# Free implementation of Bullfrog's Dungeon Keeper strategy game.
+#******************************************************************************
+# @file tool_rnctools.mk
+# A script used by GNU Make to recompile the project.
+# @par Purpose:
+# Defines make rules for tools needed to build KeeperFX.
+# Most tools can either by compiled from source or downloaded.
+# @par Comment:
+# None.
+# @author Tomasz Lis
+# @date 25 Jan 2009 - 02 Jul 2011
+# @par Copying and copyrights:
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+#******************************************************************************
+
+.PHONY: clean-rnctools deep-clean-rnctools
+
+tools: $(RNC) $(DERNC)
+
+clean-tools: clean-rnctools
+
+deep-clean-tools: deep-clean-rnctools
+
+ifneq (,$(wildcard tools/rnctools/src/dernc.c))
+
+# If we have source code of this tool, compile it
+$(RNC) $(DERNC): tools/rnctools/src/rnc.c tools/rnctools/src/dernc.c
+ $(MAKE) -C tools/rnctools
+
+clean-rnctools:
+ $(MAKE) -C tools/rnctools clean
+
+else ifneq (,$(findstring .tar.gz,$(RNCTOOLS_PACKAGE)))
+
+# If we have tar gzip prebuild, download and extract it
+$(RNC) $(DERNC): tools/rnctools/pkg/$(RNCTOOLS_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ tar -zxmUf "../../../$<"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/rnctools/pkg/$(RNCTOOLS_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(RNCTOOLS_DOWNLOAD)"
+ tar -tzf "$@.dl" >/dev/null
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-rnctools:
+ -$(RM) tools/rnctools/bin/*
+
+deep-clean-rnctools:
+ -$(RM) tools/rnctools/pkg/$(RNCTOOLS_PACKAGE)
+
+else ifneq (,$(findstring .zip,$(RNCTOOLS_PACKAGE)))
+
+# If we have zip prebuild, download and extract it
+$(RNC) $(DERNC): tools/rnctools/pkg/$(RNCTOOLS_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ unzip -DD -qo "../../../$<"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/rnctools/pkg/$(RNCTOOLS_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(RNCTOOLS_DOWNLOAD)"
+ unzip -qt "$@.dl"
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-rnctools:
+ -$(RM) tools/rnctools/bin/*
+
+deep-clean-rnctools:
+ -$(RM) tools/rnctools/pkg/$(RNCTOOLS_PACKAGE)
+
+else
+
+$(error Cannot find rnctools tool source nor prebuild. Get package or source manually.)
+
+endif
+
+#******************************************************************************
diff --git a/build/make/tool_sndbanker.mk b/build/make/tool_sndbanker.mk
new file mode 100644
index 0000000000..ef9dbbbd0d
--- /dev/null
+++ b/build/make/tool_sndbanker.mk
@@ -0,0 +1,96 @@
+#******************************************************************************
+# Free implementation of Bullfrog's Dungeon Keeper strategy game.
+#******************************************************************************
+# @file tool_sndbanker.mk
+# A script used by GNU Make to recompile the project.
+# @par Purpose:
+# Defines make rules for tools needed to build KeeperFX.
+# Most tools can either by compiled from source or downloaded.
+# @par Comment:
+# None.
+# @author Tomasz Lis
+# @date 25 Jan 2009 - 02 Jul 2011
+# @par Copying and copyrights:
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+#******************************************************************************
+
+.PHONY: clean-sndbanker deep-clean-sndbanker
+
+tools: $(WAVTODAT)
+
+clean-tools: clean-sndbanker
+
+deep-clean-tools: deep-clean-sndbanker
+
+ifneq (,$(wildcard tools/sndbanker/src/sndbanker.cpp))
+
+# If we have source code of this tool, compile it
+$(WAVTODAT): tools/sndbanker/src/sndbanker.cpp
+ $(MAKE) -C tools/sndbanker
+
+clean-sndbanker:
+ $(MAKE) -C tools/sndbanker clean
+
+else ifneq (,$(findstring .tar.gz,$(SNDBANKER_PACKAGE)))
+
+# If we have tar gzip prebuild, download and extract it
+$(WAVTODAT): tools/sndbanker/pkg/$(SNDBANKER_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ tar -zxmUf "../../../$<"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/sndbanker/pkg/$(SNDBANKER_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(SNDBANKER_DOWNLOAD)"
+ tar -tzf "$@.dl" >/dev/null
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-sndbanker:
+ -$(RM) tools/sndbanker/bin/*
+
+deep-clean-sndbanker:
+ -$(RM) tools/sndbanker/pkg/$(SNDBANKER_PACKAGE)
+
+else ifneq (,$(findstring .zip,$(SNDBANKER_PACKAGE)))
+
+# If we have zip prebuild, download and extract it
+$(WAVTODAT): tools/sndbanker/pkg/$(SNDBANKER_PACKAGE)
+ -$(ECHO) 'Extracting package: $<'
+ $(MKDIR) "$(@D)"
+ cd "$(@D)"; \
+ unzip -DD -qo "../../../$<"
+ -$(ECHO) 'Finished extracting: $<'
+ -$(ECHO) ' '
+
+tools/sndbanker/pkg/$(SNDBANKER_PACKAGE):
+ -$(ECHO) 'Downloading package: $@'
+ $(MKDIR) "$(@D)"
+ curl -L -o "$@.dl" "$(SNDBANKER_DOWNLOAD)"
+ unzip -qt "$@.dl"
+ $(MV) "$@.dl" "$@"
+ -$(ECHO) 'Finished downloading: $@'
+ -$(ECHO) ' '
+
+clean-sndbanker:
+ -$(RM) tools/sndbanker/bin/*
+
+deep-clean-sndbanker:
+ -$(RM) tools/sndbanker/pkg/$(SNDBANKER_PACKAGE)
+
+else
+
+$(error Cannot find sndbanker tool source nor prebuild. Get package or source manually.)
+
+endif
+
+#******************************************************************************
diff --git a/build/make/version.mk b/build/make/version.mk
new file mode 100644
index 0000000000..0d53c3fa8b
--- /dev/null
+++ b/build/make/version.mk
@@ -0,0 +1,6 @@
+VER_MAJOR=1
+VER_MINOR=3
+VER_RELEASE=1
+VER_BUILD=0
+
+PACKAGE_SUFFIX=
\ No newline at end of file
diff --git a/build/ver_defs.h.in b/build/ver_defs.h.in
new file mode 100644
index 0000000000..48b98d9f2c
--- /dev/null
+++ b/build/ver_defs.h.in
@@ -0,0 +1,7 @@
+#define VER_MAJOR ${VER_MAJOR}
+#define VER_MINOR ${VER_MINOR}
+#define VER_RELEASE ${VER_RELEASE}
+#define VER_BUILD ${VER_BUILD}
+#define VER_STRING "${VER_STRING}"
+#define PACKAGE_SUFFIX "${PACKAGE_SUFFIX}"
+
From 2b189e7523fd45f31d0c339a9eccff3a63d95b78 Mon Sep 17 00:00:00 2001
From: Cerwym <1760289+Cerwym@users.noreply.github.com>
Date: Mon, 23 Mar 2026 11:34:42 +0000
Subject: [PATCH 02/15] build(msvc): Add MSVC/vcpkg native Windows build
support
Introduces full MSVC/CMake build support alongside existing MinGW/Make:
- CMakeLists.txt restructured with modular cmake modules
- CMakePresets.json with MSVC (x86/x64), MinGW, and cross-compile presets
- vcpkg manifest with custom registry for centijson, enet6, astronomy, libnatpmp
- BuildTargets, Dependencies, Helpers, Platforms cmake modules
- MSVC toolchain config (static runtime, C11/C++20, /J unsigned char)
- compiler_compat.h with POSIX shims (strsep, access, mkdir, etc.)
- tools/CMakeLists.txt with find_program fallback + download extraction
- deps/CMakeLists.txt with MSVC find_package vs MinGW tarball paths
---
.gitignore | 21 +-
.gitmodules | 9 +
CMakeLists.txt | 94 ++-----
CMakePresets.json | 329 +++++++++++++++++++++++--
build/cmake/modules/BuildTargets.cmake | 140 +++++++++++
build/cmake/modules/Dependencies.cmake | 66 +++++
build/cmake/modules/Helpers.cmake | 63 +++++
build/cmake/modules/Platforms.cmake | 23 ++
build/cmake/toolchains/msvc.cmake | 74 ++++++
deps/CMakeLists.txt | 307 +++++++++++++++++------
external/vcpkg | 1 +
src/compiler_compat.h | 223 +++++++++++++++++
tools/CMakeLists.txt | 129 ++++++++--
vcpkg-configuration.json | 8 +-
vcpkg.json | 16 +-
15 files changed, 1315 insertions(+), 188 deletions(-)
create mode 100644 .gitmodules
create mode 100644 build/cmake/modules/BuildTargets.cmake
create mode 100644 build/cmake/modules/Dependencies.cmake
create mode 100644 build/cmake/modules/Helpers.cmake
create mode 100644 build/cmake/modules/Platforms.cmake
create mode 100644 build/cmake/toolchains/msvc.cmake
create mode 160000 external/vcpkg
create mode 100644 src/compiler_compat.h
diff --git a/.gitignore b/.gitignore
index 391a7776c6..e5219058d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,7 +16,6 @@
.vs/
/out/*
*.cache
-.vscode
/libexterns
docs/html
docs/latex
@@ -30,7 +29,6 @@ res/keeperfx_icon.ico
/deps/zlib
/deps/spng
/deps/astronomy
-/deps/centijson
/deps/ffmpeg
/deps/openal
/deps/luajit
@@ -38,7 +36,26 @@ res/keeperfx_icon.ico
/deps/libnatpmp
/deps/*.tar.gz
/cppcheck.cache
+
+# vcpkg installed packages — build output, never committed.
+# Placed one level up (../vcpkg_installed) so all worktrees share one copy.
+vcpkg_installed/
+
/src/ver_defs.h
keeperfx.log
crash_log.txt
.local/
+.deploy/
+
+# Python cache
+__pycache__/
+*.pyc
+*.pyo
+*.pyd
+.Python
+
+# Generated documentation/audit reports
+*_AUDIT_REPORT.md
+*_DEPENDENCIES_*.md
+*_SECURITY_AUDIT.md
+.vscode/.sonar-token
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000..3ae28b34c2
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,9 @@
+[submodule "gfx"]
+ path = gfx
+ url = https://github.com/dkfans/FXGraphics.git
+[submodule "sfx"]
+ path = sfx
+ url = https://github.com/dkfans/FXSounds.git
+[submodule "external/vcpkg"]
+ path = external/vcpkg
+ url = https://github.com/microsoft/vcpkg
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 585294eb8a..2602b6f7b0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,91 +10,33 @@ project(keeperfx C CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 20)
-# Get the abbreviated commit Id of the head.
-find_package(Git REQUIRED)
-execute_process(COMMAND "${GIT_EXECUTABLE}" describe --always OUTPUT_VARIABLE COMMIT_ID OUTPUT_STRIP_TRAILING_WHITESPACE)
-
+# Version information
set(VER_MAJOR 1)
-set(VER_MINOR 2)
-set(VER_RELEASE 0)
+set(VER_MINOR 3)
+set(VER_RELEASE 1)
set(VER_BUILD 0)
set(VER_STRING "${VER_MAJOR}.${VER_MINOR}.${VER_RELEASE}.${VER_BUILD} ${PACKAGE_SUFFIX}")
set(PACKAGE_SUFFIX "")
-set(GIT_REVISION "${COMMIT_ID}")
-# CMAKE_BINARY_DIR is defined in CMakePresets.json.
-set(KEEPERFX_VER_DEFS_H_IN ${CMAKE_SOURCE_DIR}/ver_defs.h.in)
-set(KEEPERFX_VER_DEFS_H_OUT ${CMAKE_SOURCE_DIR}/ver_defs.h)
+# Generate version header
+set(KEEPERFX_VER_DEFS_H_IN ${CMAKE_SOURCE_DIR}/build/ver_defs.h.in)
+set(KEEPERFX_VER_DEFS_H_OUT ${CMAKE_SOURCE_DIR}/src/ver_defs.h)
configure_file(${KEEPERFX_VER_DEFS_H_IN} ${KEEPERFX_VER_DEFS_H_OUT})
-find_package(SDL2 CONFIG REQUIRED)
-find_package(SDL2_image CONFIG REQUIRED)
-find_package(SDL2_mixer CONFIG REQUIRED)
-find_package(SDL2_net CONFIG REQUIRED)
-
-# Global definitions.
-add_compile_definitions(_CRT_NONSTDC_NO_WARNINGS _CRT_SECURE_NO_WARNINGS)
-
-file(GLOB_RECURSE KEEPERFX_SOURCES_C "src/*.c")
-file(GLOB_RECURSE KEEPERFX_SOURCES_CXX "src/*.cpp")
-
-# Global definitions for all targets.
-add_compile_definitions("DEBUG=$,1,0>")
-add_compile_definitions("SPNG_STATIC=1")
-
-# Add two executable targets: keeperfx and keeperfx_hvlog.
-add_executable(keeperfx ${KEEPERFX_SOURCES_C} ${KEEPERFX_SOURCES_CXX})
-
-target_compile_definitions(keeperfx PUBLIC BFDEBUG_LEVEL=0)
-target_sources(keeperfx PRIVATE "res/keeperfx_stdres.rc")
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+# Load CMake Modules
+# See: build/cmake/modules/
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/build/cmake/modules)
-add_executable(keeperfx_hvlog ${KEEPERFX_SOURCES_C} ${KEEPERFX_SOURCES_CXX})
+include(Helpers)
+include(Platforms)
+include(Dependencies)
+include(BuildTargets)
-target_compile_definitions(keeperfx_hvlog PUBLIC BFDEBUG_LEVEL=10)
-target_sources(keeperfx_hvlog PRIVATE "res/keeperfx_stdres.rc")
-
-message(STATUS "We are using ${CMAKE_CXX_COMPILER_ID}")
-
-# The default bfd linker in MinGW is extremely slow. LLVM linker (LLD) is much much faster.
-set_property(TARGET keeperfx PROPERTY LINKER_TYPE LLD)
-set_property(TARGET keeperfx_hvlog PROPERTY LINKER_TYPE LLD)
-
-set(WARNFLAGS -Wall -W -Wshadow -Wno-sign-compare -Wno-unused-parameter -Wno-strict-aliasing -Wno-unknown-pragmas -Werror)
-set(GNU_COMPILER_FLAG -march=x86-64 -fno-omit-frame-pointer -fmessage-length=0)
-set(GNU_LINK_FLAG -mwindows -Wl,--enable-auto-import)
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wimplicit")
-target_compile_options(keeperfx PRIVATE ${WARNFLAGS} ${GNU_COMPILER_FLAG})
-target_compile_options(keeperfx_hvlog PRIVATE ${WARNFLAGS} ${GNU_COMPILER_FLAG})
-target_link_options(keeperfx PRIVATE ${GNU_LINK_FLAG} -Wl,-Map,keeperfx.map)
-target_link_options(keeperfx_hvlog PRIVATE ${GNU_LINK_FLAG} -Wl,-Map,keeperfx_hvlog.map)
-target_link_libraries (keeperfx PUBLIC -static stdc++ winpthread -dynamic)
-target_link_libraries (keeperfx_hvlog PUBLIC -static stdc++ winpthread -dynamic)
-
-# System libraries.
-target_link_libraries(keeperfx PRIVATE imagehlp dbghelp)
-target_link_libraries(keeperfx_hvlog PRIVATE imagehlp dbghelp)
-
-# Go into submodules.
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+# Subdirectories
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
add_subdirectory(deps)
add_subdirectory(tools)
-# External libraries.
-target_link_libraries(keeperfx
- PRIVATE
- $,SDL2::SDL2,SDL2::SDL2-static>)
-target_link_libraries(keeperfx
- PRIVATE $,SDL2_mixer::SDL2_mixer,SDL2_mixer::SDL2_mixer-static>)
-target_link_libraries(keeperfx
- PRIVATE $,SDL2_net::SDL2_net,SDL2_net::SDL2_net-static>)
-target_link_libraries(keeperfx
- PRIVATE $,SDL2_image::SDL2_image,SDL2_image::SDL2_image-static>)
-
-target_link_libraries(keeperfx_hvlog
- PRIVATE
- $,SDL2::SDL2,SDL2::SDL2-static>)
-target_link_libraries(keeperfx_hvlog
- PRIVATE $,SDL2_mixer::SDL2_mixer,SDL2_mixer::SDL2_mixer-static>)
-target_link_libraries(keeperfx_hvlog
- PRIVATE $,SDL2_net::SDL2_net,SDL2_net::SDL2_net-static>)
-target_link_libraries(keeperfx_hvlog
- PRIVATE $,SDL2_image::SDL2_image,SDL2_image::SDL2_image-static>)
diff --git a/CMakePresets.json b/CMakePresets.json
index b606168c37..ab039111c0 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -1,4 +1,4 @@
-{
+{
"version": 3,
"configurePresets": [
{
@@ -11,10 +11,10 @@
"installDir": "${sourceDir}/out/install/${presetName}",
"cacheVariables": {
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
- "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
+ "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/external/vcpkg/scripts/buildsystems/vcpkg.cmake",
+ "VCPKG_INSTALLED_DIR": "${sourceDir}/../vcpkg_installed"
}
},
-
{
"name": "x86",
"hidden": true,
@@ -23,12 +23,13 @@
"strategy": "external"
}
},
-
{
"name": "Debug",
"displayName": "Config Debug",
"hidden": true,
- "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" }
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug"
+ }
},
{
"name": "Release",
@@ -38,7 +39,6 @@
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}
},
-
{
"name": "MSVC",
"hidden": true,
@@ -60,32 +60,166 @@
}
}
},
-
{
"name": "Debug-Clang",
"displayName": "Debug Clang",
- "inherits": [ "base", "x86", "Debug", "Clang" ],
+ "inherits": [
+ "base",
+ "x86",
+ "Debug",
+ "Clang"
+ ],
"hidden": true
},
{
"name": "Release-Clang",
"displayName": "Release Clang",
- "inherits": [ "base", "x86", "Release", "Clang" ],
+ "inherits": [
+ "base",
+ "x86",
+ "Release",
+ "Clang"
+ ],
"hidden": true
},
{
"name": "Debug-MSVC",
"displayName": "Debug MSVC",
- "inherits": [ "base", "x86", "Debug", "MSVC" ],
+ "inherits": [
+ "base",
+ "x86",
+ "Debug",
+ "MSVC"
+ ],
"hidden": true
},
{
"name": "Release-MSVC",
"displayName": "Release MSVC",
- "inherits": [ "base", "x86", "Release", "MSVC" ],
+ "inherits": [
+ "base",
+ "x86",
+ "Release",
+ "MSVC"
+ ],
"hidden": true
},
-
+ {
+ "name": "linux-base",
+ "displayName": "Linux Base",
+ "description": "Base for Linux native builds (no vcpkg toolchain; uses system packages)",
+ "hidden": true,
+ "generator": "Ninja",
+ "binaryDir": "${sourceDir}/out/build/${presetName}",
+ "installDir": "${sourceDir}/out/install/${presetName}",
+ "cacheVariables": {
+ "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
+ }
+ },
+ {
+ "name": "x64",
+ "hidden": true,
+ "architecture": {
+ "value": "x64",
+ "strategy": "external"
+ }
+ },
+ {
+ "name": "MSVC-x86-Base",
+ "hidden": true,
+ "inherits": "base",
+ "binaryDir": "${sourceDir}/out/build/${presetName}",
+ "installDir": "${sourceDir}/out/install/${presetName}",
+ "architecture": {
+ "value": "x86",
+ "strategy": "external"
+ },
+ "cacheVariables": {
+ "CMAKE_C_COMPILER": "cl.exe",
+ "CMAKE_CXX_COMPILER": "cl.exe",
+ "VCPKG_TARGET_TRIPLET": "x86-windows-static",
+ "CMAKE_POLICY_VERSION_MINIMUM": "3.5",
+ "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>"
+ }
+ },
+ {
+ "name": "MSVC-x64-Base",
+ "hidden": true,
+ "inherits": "base",
+ "binaryDir": "${sourceDir}/out/build/${presetName}",
+ "installDir": "${sourceDir}/out/install/${presetName}",
+ "architecture": {
+ "value": "x64",
+ "strategy": "external"
+ },
+ "cacheVariables": {
+ "CMAKE_C_COMPILER": "cl.exe",
+ "CMAKE_CXX_COMPILER": "cl.exe",
+ "VCPKG_TARGET_TRIPLET": "x64-windows-static",
+ "CMAKE_POLICY_VERSION_MINIMUM": "3.5",
+ "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>"
+ }
+ },
+ {
+ "name": "x86-windows-static-debug",
+ "displayName": "MSVC x86 Debug (static)",
+ "description": "Windows x86 Debug build with MSVC, static runtime linking",
+ "inherits": [
+ "MSVC-x86-Base",
+ "Debug"
+ ],
+ "cacheVariables": {
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on"
+ }
+ },
+ {
+ "name": "x86-windows-static-release",
+ "displayName": "MSVC x86 Release (static)",
+ "description": "Windows x86 Release build with MSVC, static runtime linking, optimized",
+ "inherits": [
+ "MSVC-x86-Base",
+ "Release"
+ ],
+ "cacheVariables": {
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on"
+ }
+ },
+ {
+ "name": "x64-windows-static-debug",
+ "displayName": "MSVC x64 Debug (static)",
+ "description": "Windows x64 Debug build with MSVC, static runtime linking",
+ "inherits": [
+ "MSVC-x64-Base",
+ "Debug"
+ ],
+ "cacheVariables": {
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on"
+ }
+ },
+ {
+ "name": "x64-windows-static-release",
+ "displayName": "MSVC x64 Release (static)",
+ "description": "Windows x64 Release build with MSVC, static runtime linking, optimized",
+ "inherits": [
+ "MSVC-x64-Base",
+ "Release"
+ ],
+ "cacheVariables": {
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on"
+ }
+ },
+ {
+ "name": "cross-base",
+ "displayName": "Cross-Compile Base",
+ "description": "Base for Docker/Linux cross-compile builds (no vcpkg; toolchain set per-preset)",
+ "hidden": true,
+ "generator": "Ninja",
+ "binaryDir": "${sourceDir}/out/build/${presetName}",
+ "installDir": "${sourceDir}/out/install/${presetName}",
+ "cacheVariables": {
+ "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
+ }
+ },
{
"name": "MingGW32-Base",
"hidden": true,
@@ -104,11 +238,19 @@
}
}
},
-
{
"name": "x86-MinGW32-Debug",
"displayName": "x86-MinGW32-Debug",
- "inherits": [ "MingGW32-Base", "x86", "Debug" ],
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ },
+ "inherits": [
+ "MingGW32-Base",
+ "x86",
+ "Debug"
+ ],
"cacheVariables": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "on"
}
@@ -116,10 +258,108 @@
{
"name": "x86-MinGW32-Release",
"displayName": "x86-MinGW32-Release",
- "inherits": [ "MingGW32-Base", "x86", "Release" ],
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Windows"
+ },
+ "inherits": [
+ "MingGW32-Base",
+ "x86",
+ "Release"
+ ],
+ "cacheVariables": {
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on"
+ }
+ },
+ {
+ "name": "windows-x86-release",
+ "displayName": "Windows x86 Release (Docker/Linux cross-compile)",
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Linux"
+ },
+ "description": "Cross-compile for Windows x86 from Linux using i686-w64-mingw32. Designed for use inside the keeperfx-build-mingw32 Docker image.",
+ "inherits": [
+ "cross-base",
+ "Release"
+ ],
+ "cacheVariables": {
+ "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/build/cmake/mingw32.cmake",
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on",
+ "CMAKE_BUILD_TYPE": "Release"
+ }
+ },
+ {
+ "name": "windows-x86-debug",
+ "displayName": "Windows x86 Debug (Docker/Linux cross-compile)",
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Linux"
+ },
+ "description": "Cross-compile for Windows x86 from Linux using i686-w64-mingw32, with full debug symbols. For use inside the keeperfx-build-mingw32 Docker image.",
+ "inherits": [
+ "cross-base",
+ "Debug"
+ ],
+ "cacheVariables": {
+ "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/build/cmake/mingw32.cmake",
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on"
+ }
+ },
+ {
+ "name": "windows-x64-release",
+ "displayName": "Windows x64 Release (Docker/Linux cross-compile)",
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Linux"
+ },
+ "description": "Cross-compile for Windows x64 from Linux using x86_64-w64-mingw32. Designed for use inside the keeperfx-build-mingw64 Docker image.",
+ "inherits": [
+ "cross-base",
+ "Release"
+ ],
+ "cacheVariables": {
+ "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/build/cmake/mingw64.cmake",
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on",
+ "CMAKE_BUILD_TYPE": "Release"
+ }
+ },
+ {
+ "name": "linux-x64-release",
+ "displayName": "Linux x64 Release",
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Linux"
+ },
+ "description": "Linux native build using system packages",
+ "inherits": [
+ "linux-base",
+ "Release"
+ ],
"cacheVariables": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "on"
}
+ },
+ {
+ "name": "linux-x64-asan",
+ "displayName": "Linux x64 ASAN",
+ "condition": {
+ "type": "equals",
+ "lhs": "${hostSystemName}",
+ "rhs": "Linux"
+ },
+ "description": "Linux native build with AddressSanitizer + UndefinedBehaviorSanitizer",
+ "inherits": "linux-base",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "RelWithDebInfo",
+ "KEEPERFX_SANITIZERS": "ON",
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on"
+ }
}
],
"buildPresets": [
@@ -130,7 +370,6 @@
"verbose": true,
"configurePreset": "Debug"
},
-
{
"name": "StandardLog",
"hidden": true,
@@ -143,7 +382,6 @@
"inherits": "Base",
"targets": "keeperfx_hvlog"
},
-
{
"name": "Debug-Clang-Standard",
"displayName": "Debug Clang Standard",
@@ -192,7 +430,6 @@
"inherits": "HeavyLog",
"configurePreset": "Release-MSVC"
},
-
{
"name": "Debug-MinGW32-Standard",
"displayName": "Debug MinGW32 Standard",
@@ -216,6 +453,60 @@
"displayName": "Release MinGW32 HeavyLog",
"inherits": "HeavyLog",
"configurePreset": "x86-MinGW32-Release"
+ },
+ {
+ "name": "windows-x86-release",
+ "displayName": "Windows x86 Release",
+ "inherits": "StandardLog",
+ "configurePreset": "windows-x86-release"
+ },
+ {
+ "name": "windows-x86-debug",
+ "displayName": "Windows x86 Debug",
+ "inherits": "StandardLog",
+ "configurePreset": "windows-x86-debug"
+ },
+ {
+ "name": "x86-windows-static-debug",
+ "displayName": "MSVC x86 Debug (static)",
+ "inherits": "StandardLog",
+ "configurePreset": "x86-windows-static-debug"
+ },
+ {
+ "name": "x86-windows-static-release",
+ "displayName": "MSVC x86 Release (static)",
+ "inherits": "StandardLog",
+ "configurePreset": "x86-windows-static-release"
+ },
+ {
+ "name": "x64-windows-static-debug",
+ "displayName": "MSVC x64 Debug (static)",
+ "inherits": "StandardLog",
+ "configurePreset": "x64-windows-static-debug"
+ },
+ {
+ "name": "x64-windows-static-release",
+ "displayName": "MSVC x64 Release (static)",
+ "inherits": "StandardLog",
+ "configurePreset": "x64-windows-static-release"
+ },
+ {
+ "name": "windows-x64-release",
+ "displayName": "Windows x64 Release",
+ "inherits": "StandardLog",
+ "configurePreset": "windows-x64-release"
+ },
+ {
+ "name": "linux-x64-release",
+ "displayName": "Linux x64 Release",
+ "inherits": "StandardLog",
+ "configurePreset": "linux-x64-release"
+ },
+ {
+ "name": "linux-x64-asan",
+ "displayName": "Linux x64 ASAN",
+ "inherits": "Base",
+ "configurePreset": "linux-x64-asan"
}
]
-}
+}
\ No newline at end of file
diff --git a/build/cmake/modules/BuildTargets.cmake b/build/cmake/modules/BuildTargets.cmake
new file mode 100644
index 0000000000..43bd26fa01
--- /dev/null
+++ b/build/cmake/modules/BuildTargets.cmake
@@ -0,0 +1,140 @@
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+# BuildTargets.cmake — Target definitions (keeperfx, keeperfx_hvlog, tests)
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+# ━━━ Source File Collection ━━━
+file(GLOB_RECURSE KEEPERFX_SOURCES_C "src/*.c")
+file(GLOB_RECURSE KEEPERFX_SOURCES_CXX "src/*.cpp")
+
+# Exclude stub files — added back explicitly when needed
+list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/bflib_cpu_stub\\.c$")
+list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/bflib_network_stub\\.c$")
+
+# OpenGL renderer exclusion
+if(NOT KEEPERFX_RENDERER_OPENGL)
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/renderer/RendererOpenGL\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/platform_gl_sdl2\\.cpp$")
+endif()
+
+# ━━━ Networking Exclusions ━━━
+if(NOT KEEPERFX_NETWORKING)
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/api\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/bflib_tcpsp\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/bflib_base_tcp\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/bflib_client_tcp\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/bflib_server_tcp\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/bflib_enet\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/net_portforward\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/bflib_netsession\\.c$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/bflib_netsp\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/bflib_network\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/bflib_network_exchange\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/net_resync\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/net_input_lag\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/net_received_packets\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/net_redundant_packets\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/net_checksums\\.c$")
+ list(FILTER KEEPERFX_SOURCES_C EXCLUDE REGEX ".*/net_game\\.c$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/bflib_enet\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/bflib_base_tcp\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/bflib_client_tcp\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/bflib_server_tcp\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/net_portforward\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/net_resync\\.cpp$")
+ list(APPEND KEEPERFX_SOURCES_C "src/bflib_network_stub.c")
+endif()
+
+# ━━━ Desktop platform filtering (Windows vs Linux) ━━━
+if(WIN32 OR MINGW)
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/platform/PlatformLinux\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/src/linux\\.cpp$")
+elseif(UNIX AND NOT APPLE)
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/platform/PlatformWindows\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/src/windows\\.cpp$")
+elseif(APPLE)
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/platform/PlatformLinux\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/platform/PlatformWindows\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/src/linux\\.cpp$")
+ list(FILTER KEEPERFX_SOURCES_CXX EXCLUDE REGEX ".*/src/windows\\.cpp$")
+endif()
+
+# ━━━ Main Targets: keeperfx & keeperfx_hvlog ━━━
+add_executable(keeperfx ${KEEPERFX_SOURCES_C} ${KEEPERFX_SOURCES_CXX})
+if(MSVC)
+ set_target_properties(keeperfx PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/.deploy"
+ RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_SOURCE_DIR}/.deploy"
+ )
+endif()
+target_include_directories(keeperfx PRIVATE src deps/centitoml deps/centijson/include)
+target_compile_definitions(keeperfx PUBLIC BFDEBUG_LEVEL=0)
+if(WIN32)
+ target_sources(keeperfx PRIVATE "res/keeperfx_stdres.rc")
+endif()
+apply_keeperfx_warnings(keeperfx)
+apply_keeperfx_link_flags(keeperfx)
+apply_windows_system_libs(keeperfx)
+
+# Link SDL2 (vcpkg static targets have -static suffix; mingw/Linux use plain names)
+macro(kfx_link_sdl2_target TARGET_NAME)
+ if(TARGET SDL2::SDL2-static)
+ target_link_libraries(${TARGET_NAME} PRIVATE SDL2::SDL2-static)
+ elseif(TARGET SDL2::SDL2)
+ target_link_libraries(${TARGET_NAME} PRIVATE SDL2::SDL2 SDL2::SDL2main)
+ elseif(TARGET SDL2)
+ target_link_libraries(${TARGET_NAME} PRIVATE SDL2)
+ endif()
+ if(TARGET SDL2_image::SDL2_image-static)
+ target_link_libraries(${TARGET_NAME} PRIVATE SDL2_image::SDL2_image-static)
+ elseif(TARGET SDL2_image::SDL2_image)
+ target_link_libraries(${TARGET_NAME} PRIVATE SDL2_image::SDL2_image)
+ endif()
+ if(TARGET SDL2_mixer::SDL2_mixer-static)
+ target_link_libraries(${TARGET_NAME} PRIVATE SDL2_mixer::SDL2_mixer-static)
+ elseif(TARGET SDL2_mixer::SDL2_mixer)
+ target_link_libraries(${TARGET_NAME} PRIVATE SDL2_mixer::SDL2_mixer)
+ endif()
+ if(KEEPERFX_NETWORKING)
+ if(TARGET SDL2_net::SDL2_net-static)
+ target_link_libraries(${TARGET_NAME} PRIVATE SDL2_net::SDL2_net-static)
+ elseif(TARGET SDL2_net::SDL2_net)
+ target_link_libraries(${TARGET_NAME} PRIVATE SDL2_net::SDL2_net)
+ endif()
+ endif()
+endmacro()
+kfx_link_sdl2_target(keeperfx)
+
+add_executable(keeperfx_hvlog ${KEEPERFX_SOURCES_C} ${KEEPERFX_SOURCES_CXX})
+target_include_directories(keeperfx_hvlog PRIVATE src deps/centitoml deps/centijson/include)
+target_compile_definitions(keeperfx_hvlog PUBLIC BFDEBUG_LEVEL=10)
+if(WIN32)
+ target_sources(keeperfx_hvlog PRIVATE "res/keeperfx_stdres.rc")
+endif()
+apply_keeperfx_warnings(keeperfx_hvlog)
+apply_keeperfx_link_flags(keeperfx_hvlog)
+apply_windows_system_libs(keeperfx_hvlog)
+
+kfx_link_sdl2_target(keeperfx_hvlog)
+
+# ━━━ Dependent Platform Libraries ━━━
+# Linked AFTER targets are created (see `add_subdirectory(deps)` in root CMakeLists.txt)
+
+# ━━━ Test Target ━━━
+file(GLOB TEST_SOURCES "tests/*.cpp")
+add_library(cunit_static STATIC
+ "deps/CUnit-2.1-3/CUnit/Sources/Basic/Basic.c"
+ "deps/CUnit-2.1-3/CUnit/Sources/Framework/TestDB.c"
+ "deps/CUnit-2.1-3/CUnit/Sources/Framework/CUError.c"
+ "deps/CUnit-2.1-3/CUnit/Sources/Framework/TestRun.c"
+ "deps/CUnit-2.1-3/CUnit/Sources/Framework/Util.c"
+)
+target_include_directories(cunit_static PUBLIC "deps/CUnit-2.1-3/CUnit/Headers")
+
+add_executable(tests ${TEST_SOURCES} ${KEEPERFX_SOURCES_C} ${KEEPERFX_SOURCES_CXX})
+target_compile_definitions(tests PUBLIC BFDEBUG_LEVEL=0)
+target_link_libraries(tests PRIVATE cunit_static)
+apply_keeperfx_warnings(tests)
+apply_keeperfx_link_flags(tests)
+apply_windows_system_libs(tests)
+
+message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID}")
diff --git a/build/cmake/modules/Dependencies.cmake b/build/cmake/modules/Dependencies.cmake
new file mode 100644
index 0000000000..cb7a7fb19b
--- /dev/null
+++ b/build/cmake/modules/Dependencies.cmake
@@ -0,0 +1,66 @@
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+# Dependencies.cmake — find_package() for all dependencies
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+# ━━━ SDL2 & Graphics ━━━
+# MinGW cross-compile: force static SDL2 libs (no runtime DLLs)
+if(MINGW OR CMAKE_CROSSCOMPILING)
+ set(SDL2_USE_STATIC_LIBS ON)
+ set(SDL2IMAGE_STATIC ON)
+ set(SDL2MIXER_STATIC ON)
+ set(SDL2NET_STATIC ON)
+endif()
+
+find_package(SDL2 CONFIG REQUIRED)
+find_package(SDL2_image CONFIG REQUIRED)
+find_package(SDL2_mixer CONFIG REQUIRED)
+if(KEEPERFX_NETWORKING)
+ find_package(SDL2_net CONFIG REQUIRED)
+endif()
+
+# ━━━ OpenGL Renderer (optional) ━━━
+if(KEEPERFX_RENDERER_OPENGL)
+ find_package(glad CONFIG QUIET)
+ if(glad_FOUND)
+ kfx_status("DEPS" "OpenGL renderer backend enabled (glad found)")
+ else()
+ kfx_status("DEPS" "OpenGL renderer backend disabled (glad not found; install via vcpkg)")
+ set(KEEPERFX_RENDERER_OPENGL OFF)
+ endif()
+endif()
+
+# ━━━ Audio & Codecs (vcpkg) ━━━
+find_package(FFmpeg MODULE QUIET)
+if(FFmpeg_FOUND)
+ kfx_status("DEPS" "FFmpeg found (vcpkg)")
+else()
+ kfx_status("DEPS" "FFmpeg not yet in vcpkg — will use fallback from deps/ tarballs")
+endif()
+
+find_package(OpenAL CONFIG QUIET)
+if(OpenAL_FOUND OR OPENAL_FOUND)
+ kfx_status("DEPS" "OpenAL found (vcpkg)")
+else()
+ kfx_status("DEPS" "OpenAL not found — will use fallback from deps/ tarballs")
+endif()
+
+# ━━━ Networking ━━━
+find_package(enet CONFIG QUIET)
+if(enet_FOUND)
+ kfx_status("DEPS" "enet found (vcpkg)")
+else()
+ kfx_status("DEPS" "enet not found via vcpkg — will use fallback from deps/ tarballs")
+endif()
+
+# ━━━ JSON (centijson) ━━━
+find_package(centijson CONFIG QUIET)
+if(centijson_FOUND)
+ kfx_status("DEPS" "centijson found (vcpkg)")
+else()
+ kfx_status("DEPS" "centijson not found via vcpkg — will build from deps/ sources")
+endif()
+
+# ━━━ Global Definitions (all platforms) ━━━
+add_compile_definitions(_CRT_NONSTDC_NO_WARNINGS _CRT_SECURE_NO_WARNINGS)
+add_compile_definitions("DEBUG=$,1,0>")
+add_compile_definitions("SPNG_STATIC=1")
diff --git a/build/cmake/modules/Helpers.cmake b/build/cmake/modules/Helpers.cmake
new file mode 100644
index 0000000000..76f2f18c93
--- /dev/null
+++ b/build/cmake/modules/Helpers.cmake
@@ -0,0 +1,63 @@
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+# Helpers.cmake — Common functions and macros
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+# Log a status message with [PREFIX] for clarity
+function(kfx_status PREFIX MESSAGE)
+ message(STATUS "[${PREFIX}] ${MESSAGE}")
+endfunction()
+
+# Apply common compilation flags to a target based on platform
+function(apply_keeperfx_warnings TARGET)
+ if(MSVC)
+ # MSVC: use /W3, suppress common cross-platform noise
+ target_compile_options(${TARGET} PRIVATE
+ /W3
+ /wd4996 # deprecated functions
+ /wd4267 # size_t conversion
+ /wd4244 # float/int conversion
+ /wd4305 # truncation
+ /wd4018 # signed/unsigned mismatch
+ /wd4146 # unary minus on unsigned
+ /wd4101 # unreferenced local variable
+ )
+ return()
+ endif()
+
+ set(WARNFLAGS -Wall -W -Wshadow -Wno-sign-compare -Wno-unused-parameter -Wno-strict-aliasing -Wno-unknown-pragmas -Werror)
+
+ # Desktop: MinGW or native Linux
+ set(GNU_COMPILER_FLAG -march=x86-64 -fno-omit-frame-pointer -fmessage-length=0)
+ target_compile_options(${TARGET} PRIVATE ${WARNFLAGS} ${GNU_COMPILER_FLAG})
+endfunction()
+
+# Apply common link options to a target based on platform
+function(apply_keeperfx_link_flags TARGET)
+ if(MSVC)
+ # MSVC: Windows subsystem, no GCC-style flags.
+ # /MANIFEST:NO disables the auto-generated manifest because keeperfx_stdres.rc
+ # already embeds the manifest via RT_MANIFEST, and duplicate manifests cause LNK error.
+ target_link_options(${TARGET} PRIVATE /SUBSYSTEM:WINDOWS /MANIFEST:NO)
+ return()
+ endif()
+
+ if(WIN32 OR MINGW)
+ # MinGW: Windows subsystem + map file
+ target_link_options(${TARGET} PRIVATE -mwindows -Wl,--enable-auto-import -Wl,-Map,${TARGET}.map)
+ target_link_libraries(${TARGET} PUBLIC -static stdc++ winpthread -dynamic)
+ # Use LLVM linker (LLD) for faster linking
+ set_property(TARGET ${TARGET} PROPERTY LINKER_TYPE LLD)
+ else()
+ # Linux: pthreads + map file
+ target_link_options(${TARGET} PRIVATE -Wl,-Map,${TARGET}.map)
+ find_package(Threads REQUIRED)
+ target_link_libraries(${TARGET} PUBLIC Threads::Threads)
+ endif()
+endfunction()
+
+# Apply system libraries (Windows only: imagehlp, dbghelp, ole32, uuid, winmm, ws2_32)
+function(apply_windows_system_libs TARGET)
+ if(WIN32)
+ target_link_libraries(${TARGET} PRIVATE imagehlp dbghelp ole32 uuid winmm ws2_32)
+ endif()
+endfunction()
diff --git a/build/cmake/modules/Platforms.cmake b/build/cmake/modules/Platforms.cmake
new file mode 100644
index 0000000000..6f96228a31
--- /dev/null
+++ b/build/cmake/modules/Platforms.cmake
@@ -0,0 +1,23 @@
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+# Platforms.cmake — Platform detection and build options
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+message(STATUS "Building for desktop platform")
+
+# Build options
+option(KEEPERFX_NETWORKING "Build with multiplayer networking support" ON)
+option(KEEPERFX_RENDERER_OPENGL "Enable the OpenGL renderer backend (desktop only)" ON)
+option(KFX_DEBUG_MEMORY "Enable KfxAlloc guard zones and per-site tracking" OFF)
+
+# Apply compile definitions
+if(KEEPERFX_NETWORKING)
+ add_compile_definitions("KEEPERFX_NETWORKING=1")
+endif()
+
+if(KFX_DEBUG_MEMORY)
+ add_compile_definitions("KFX_DEBUG_MEMORY=1")
+endif()
+
+add_compile_definitions("KEEPERFX_LUA_AVAILABLE=1")
+add_compile_definitions("SDL_MIXER_AVAILABLE=1")
+
diff --git a/build/cmake/toolchains/msvc.cmake b/build/cmake/toolchains/msvc.cmake
new file mode 100644
index 0000000000..9d0734de7e
--- /dev/null
+++ b/build/cmake/toolchains/msvc.cmake
@@ -0,0 +1,74 @@
+# MSVC Toolchain Configuration
+# Configures CMake to use Microsoft Visual C++ (cl.exe) for Windows native builds.
+# Triplets: x86-windows-static, x64-windows-static
+
+set(CMAKE_SYSTEM_NAME Windows)
+
+# Compiler Configuration
+set(CMAKE_C_COMPILER cl.exe CACHE STRING "C compiler" FORCE)
+set(CMAKE_CXX_COMPILER cl.exe CACHE STRING "C++ compiler" FORCE)
+set(CMAKE_RC_COMPILER rc.exe CACHE STRING "Resource compiler" FORCE)
+
+# Force MSVC detection
+set(MSVC TRUE CACHE BOOL "Force MSVC detection" FORCE)
+set(CMAKE_CXX_COMPILER_ID "MSVC" CACHE STRING "CXX compiler ID" FORCE)
+set(CMAKE_C_COMPILER_ID "MSVC" CACHE STRING "C compiler ID" FORCE)
+
+# Disable compiler checks (assumes MSVC environment already set up)
+set(CMAKE_C_COMPILER_FORCED TRUE)
+set(CMAKE_CXX_COMPILER_FORCED TRUE)
+
+# Language Configuration
+set(CMAKE_C_STANDARD 11 CACHE STRING "C standard")
+set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard")
+
+# Compiler Flags
+# /J: unsigned char by default (GCC compatibility); matches MinGW behavior
+# /fp:precise: Floating point model (default, but explicit)
+# /W4: Warning level 4 (most rigorous)
+# /WX: Treat warnings as errors (matches MinGW -Werror behavior, can be relaxed)
+
+# /std:clatest: Enable latest C standard preview (C23) — required for typeof keyword
+# used in config.h field() macro (offsetof(typeof(expr), member) is a C23 pattern).
+set(MSVC_COMMON_FLAGS "/J /fp:precise /W4 /std:clatest")
+
+# For Release: /O2 (optimize for speed), /Gy (function-level linking)
+# For Debug: /Od (disable optimizations), /Zi (generate program database)
+string(APPEND CMAKE_C_FLAGS "${MSVC_COMMON_FLAGS}")
+string(APPEND CMAKE_CXX_FLAGS "${MSVC_COMMON_FLAGS}")
+
+string(APPEND CMAKE_C_FLAGS_RELEASE " /O2 /Gy")
+string(APPEND CMAKE_CXX_FLAGS_RELEASE " /O2 /Gy")
+
+string(APPEND CMAKE_C_FLAGS_DEBUG " /Od /Zi /RTC1")
+string(APPEND CMAKE_CXX_FLAGS_DEBUG " /Od /Zi /RTC1")
+
+# C4996: 'function': was declared deprecated
+# MSVC often warns about POSIX functions like fopen (prefer fopen_s).
+# For compatibility with cross-platform code (GCC doesn't warn), suppress this.
+# C4267: 'var': conversion from 'size_t' to 'type', possible loss of data
+# Intentionally suppressed for cross-compilation compatibility.
+string(APPEND CMAKE_C_FLAGS " /wd4996 /wd4267")
+string(APPEND CMAKE_CXX_FLAGS " /wd4996 /wd4267")
+
+# Runtime Library Linking
+# /MT: Multithreaded static runtime (Release)
+# /MD: Multithreaded dynamic runtime (Debug with DLL)
+# CMake handles most of this automatically via CMAKE_MSVC_RUNTIME_LIBRARY,
+# but we can explicitly set it for consistency.
+set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>")
+
+# Linker Configuration
+# /SUBSYSTEM:CONSOLE (default for executables)
+# /OPT:REF (remove unreferenced functions)
+string(APPEND CMAKE_EXE_LINKER_FLAGS " /OPT:REF")
+string(APPEND CMAKE_SHARED_LINKER_FLAGS " /OPT:REF")
+
+# vcpkg triplet configuration (auto-set by CMakePresets.json, but documented here)
+# When using this toolchain with vcpkg, ensure CMakePresets.json sets:
+# "cacheVariables": {
+# "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/cmake/vcpkg.cmake",
+# "VCPKG_TARGET_TRIPLET": "x86-windows-static" or "x64-windows-static"
+# }
+
+message(STATUS "MSVC Toolchain: cl.exe, C11/C++17, /MT (static runtime), triplet: ${VCPKG_TARGET_TRIPLET}")
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index 4f6f3d7fc9..abb25514e9 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -1,127 +1,284 @@
cmake_minimum_required(VERSION 3.20)
+function(kfx_resolve_dep_archive archive_name archive_url)
+ set(archive_dst "${CMAKE_SOURCE_DIR}/deps/${archive_name}")
-if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/enet )
- file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/enet)
- if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/enet-mingw32.tar.gz )
- file(DOWNLOAD https://github.com/dkfans/kfx-deps/releases/download/initial/enet-mingw32.tar.gz ${CMAKE_SOURCE_DIR}/deps/enet-mingw32.tar.gz SHOW_PROGRESS)
+ if(NOT EXISTS "${archive_dst}" AND DEFINED ENV{KFX_DEPS_CACHE} AND NOT "$ENV{KFX_DEPS_CACHE}" STREQUAL "")
+ set(archive_cache_src "$ENV{KFX_DEPS_CACHE}/${archive_name}")
+ if(EXISTS "${archive_cache_src}")
+ message(STATUS "Using cached dependency archive: ${archive_name}")
+ execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${archive_cache_src}" "${archive_dst}")
+ endif()
endif()
- execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/enet-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/enet)
+ if(NOT EXISTS "${archive_dst}")
+ file(DOWNLOAD "${archive_url}" "${archive_dst}" SHOW_PROGRESS)
+ endif()
+
+ file(SIZE "${archive_dst}" archive_size)
+ if(archive_size EQUAL 0)
+ message(FATAL_ERROR "Dependency archive ${archive_name} is empty. URL: ${archive_url}")
+ endif()
+endfunction()
+
+
+if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/enet6/lib/libenet6.a )
+ # Skip tarball extraction if enet is found via vcpkg (MSVC builds)
+ if(NOT enet_FOUND)
+ file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/enet6)
+ kfx_resolve_dep_archive("enet6-mingw32.tar.gz" "https://github.com/dkfans/kfx-deps/releases/download/20260310/enet6-mingw32.tar.gz")
+
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/enet6-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/enet6)
+ endif()
endif()
-if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/zlib )
+if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/zlib/libz.a )
file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/zlib)
- if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/zlib-mingw32.tar.gz )
- file(DOWNLOAD https://github.com/dkfans/kfx-deps/releases/download/initial/zlib-mingw32.tar.gz ${CMAKE_SOURCE_DIR}/deps/zlib-mingw32.tar.gz SHOW_PROGRESS)
- endif()
+ kfx_resolve_dep_archive("zlib-mingw32.tar.gz" "https://github.com/dkfans/kfx-deps/releases/download/initial/zlib-mingw32.tar.gz")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/zlib-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/zlib)
endif()
-if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/spng )
+if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/spng/libspng.a )
file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/spng)
- if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/spng-mingw32.tar.gz )
- file(DOWNLOAD https://github.com/dkfans/kfx-deps/releases/download/initial/spng-mingw32.tar.gz ${CMAKE_SOURCE_DIR}/deps/spng-mingw32.tar.gz SHOW_PROGRESS)
- endif()
+ kfx_resolve_dep_archive("spng-mingw32.tar.gz" "https://github.com/dkfans/kfx-deps/releases/download/initial/spng-mingw32.tar.gz")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/spng-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/spng)
endif()
-if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/astronomy )
+if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/astronomy/libastronomy.a )
file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/astronomy)
- if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/astronomy-mingw32.tar.gz )
- file(DOWNLOAD https://github.com/dkfans/kfx-deps/releases/download/initial/astronomy-mingw32.tar.gz ${CMAKE_SOURCE_DIR}/deps/astronomy-mingw32.tar.gz SHOW_PROGRESS)
- endif()
+ kfx_resolve_dep_archive("astronomy-mingw32.tar.gz" "https://github.com/dkfans/kfx-deps/releases/download/initial/astronomy-mingw32.tar.gz")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/astronomy-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/astronomy)
endif()
-if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/centijson )
- file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/centijson)
- if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/centijson-mingw32.tar.gz )
- file(DOWNLOAD https://github.com/dkfans/kfx-deps/releases/download/initial/centijson-mingw32.tar.gz ${CMAKE_SOURCE_DIR}/deps/centijson-mingw32.tar.gz SHOW_PROGRESS)
+if( NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/centijson/libjson.a )
+ # Skip tarball extraction if centijson is found via vcpkg (MSVC builds)
+ if(NOT centijson_FOUND)
+ file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/centijson)
+ kfx_resolve_dep_archive("centijson-mingw32.tar.gz" "https://github.com/dkfans/kfx-deps/releases/download/initial/centijson-mingw32.tar.gz")
+
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/centijson-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/centijson)
+ endif()
+endif()
+
+if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/ffmpeg/libavcodec/libavcodec.a)
+ # Skip tarball extraction if FFmpeg is found via vcpkg (MSVC builds)
+ if(NOT FFmpeg_FOUND)
+ file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/ffmpeg)
+ kfx_resolve_dep_archive("ffmpeg-mingw32.tar.gz" "https://github.com/dkfans/kfx-deps/releases/download/initial/ffmpeg-mingw32.tar.gz")
+
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/ffmpeg-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/ffmpeg)
endif()
+endif()
+
+if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/luajit/lib/libluajit.a)
+ file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/luajit)
+ kfx_resolve_dep_archive("luajit-mingw32.tar.gz" "https://github.com/dkfans/kfx-deps/releases/download/20250418/luajit-mingw32.tar.gz")
- execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/centijson-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/centijson)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/luajit-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/luajit)
endif()
-if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/ffmpeg)
- file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/ffmpeg)
- if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/ffmpeg-mingw32.tar.gz)
- file(DOWNLOAD https://github.com/dkfans/kfx-deps/releases/download/initial/ffmpeg-mingw32.tar.gz ${CMAKE_SOURCE_DIR}/deps/ffmpeg-mingw32.tar.gz SHOW_PROGRESS)
+if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/openal/libOpenAL32.a)
+ # Skip tarball extraction if OpenAL is found via vcpkg (MSVC builds)
+ if(NOT OpenAL_FOUND AND NOT OPENAL_FOUND)
+ file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/openal)
+ kfx_resolve_dep_archive("openal-mingw32.tar.gz" "https://github.com/dkfans/kfx-deps/releases/download/20260310/openal-mingw32.tar.gz")
+
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/openal-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/openal)
endif()
+endif()
+
+if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/miniupnpc/libminiupnpc.a)
+ file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/miniupnpc)
+ kfx_resolve_dep_archive("miniupnpc-mingw32.tar.gz" "https://github.com/dkfans/kfx-deps/releases/download/20260102/miniupnpc-mingw32.tar.gz")
- execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/ffmpeg-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/ffmpeg)
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/miniupnpc-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/miniupnpc)
endif()
-## enet
-add_library(enet_static STATIC IMPORTED)
-set_target_properties(enet_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/enet/libenet.a)
-set_target_properties(enet_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/enet/include)
-target_link_libraries(enet_static INTERFACE ws2_32 winmm)
-target_link_libraries(keeperfx PUBLIC enet_static)
-target_link_libraries(keeperfx_hvlog PUBLIC enet_static)
+if(NOT EXISTS ${CMAKE_SOURCE_DIR}/deps/libnatpmp/libnatpmp.a)
+ file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/libnatpmp)
+ kfx_resolve_dep_archive("libnatpmp-mingw32.tar.gz" "https://github.com/dkfans/kfx-deps/releases/download/20260102/libnatpmp-mingw32.tar.gz")
+
+ execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/deps/libnatpmp-mingw32.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/deps/libnatpmp)
+endif()
+
+## enet6
+if(MSVC)
+ find_package(enet6 CONFIG REQUIRED)
+ target_link_libraries(keeperfx PUBLIC enet6::enet6)
+ target_link_libraries(keeperfx_hvlog PUBLIC enet6::enet6)
+else()
+ add_library(enet_static STATIC IMPORTED)
+ set_target_properties(enet_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/enet6/lib/libenet6.a)
+ set_target_properties(enet_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/enet6/include)
+ target_link_libraries(enet_static INTERFACE ws2_32 winmm)
+ target_link_libraries(keeperfx PUBLIC enet_static)
+ target_link_libraries(keeperfx_hvlog PUBLIC enet_static)
+endif()
## spng
-add_library(spng_static STATIC IMPORTED)
-set_target_properties(spng_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/spng/libspng.a)
-set_target_properties(spng_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/spng/include)
-target_link_libraries(keeperfx PUBLIC spng_static)
-target_link_libraries(keeperfx_hvlog PUBLIC spng_static)
+if(MSVC)
+ find_package(SPNG CONFIG REQUIRED)
+ target_link_libraries(keeperfx PUBLIC $,spng::spng,spng::spng_static>)
+ target_link_libraries(keeperfx_hvlog PUBLIC $,spng::spng,spng::spng_static>)
+else()
+ add_library(spng_static STATIC IMPORTED)
+ set_target_properties(spng_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/spng/libspng.a)
+ set_target_properties(spng_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/spng/include)
+ target_link_libraries(keeperfx PUBLIC spng_static)
+ target_link_libraries(keeperfx_hvlog PUBLIC spng_static)
+endif()
## centijson
-add_library(centijson_static STATIC IMPORTED)
-set_target_properties(centijson_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/centijson/libjson.a)
-set_target_properties(centijson_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/centijson/include)
-target_link_libraries(keeperfx PUBLIC centijson_static)
-target_link_libraries(keeperfx_hvlog PUBLIC centijson_static)
+if(MSVC)
+ find_package(centijson CONFIG REQUIRED)
+ target_link_libraries(keeperfx PUBLIC centijson::centijson)
+ target_link_libraries(keeperfx_hvlog PUBLIC centijson::centijson)
+else()
+ add_library(centijson_static STATIC IMPORTED)
+ set_target_properties(centijson_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/centijson/libjson.a)
+ set_target_properties(centijson_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/centijson/include)
+ target_link_libraries(keeperfx PUBLIC centijson_static)
+ target_link_libraries(keeperfx_hvlog PUBLIC centijson_static)
+endif()
## astronomy.
-add_library(astronomy_static STATIC IMPORTED)
-set_target_properties(astronomy_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/astronomy/libastronomy.a)
-set_target_properties(astronomy_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/astronomy/include)
-target_link_libraries(keeperfx PUBLIC astronomy_static)
-target_link_libraries(keeperfx_hvlog PUBLIC astronomy_static)
+if(MSVC)
+ find_package(astronomy CONFIG REQUIRED)
+ target_link_libraries(keeperfx PUBLIC astronomy::astronomy)
+ target_link_libraries(keeperfx_hvlog PUBLIC astronomy::astronomy)
+else()
+ add_library(astronomy_static STATIC IMPORTED)
+ set_target_properties(astronomy_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/astronomy/libastronomy.a)
+ set_target_properties(astronomy_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/astronomy/include)
+ target_link_libraries(keeperfx PUBLIC astronomy_static)
+ target_link_libraries(keeperfx_hvlog PUBLIC astronomy_static)
+endif()
## zlib
-add_library(zlib_static STATIC IMPORTED)
-set_target_properties(zlib_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/zlib/libz.a)
-set_target_properties(zlib_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/zlib/include)
-target_link_libraries(keeperfx PUBLIC zlib_static)
-target_link_libraries(keeperfx_hvlog PUBLIC zlib_static)
+if(MSVC)
+ find_package(ZLIB REQUIRED)
+ target_link_libraries(keeperfx PUBLIC ZLIB::ZLIB)
+ target_link_libraries(keeperfx_hvlog PUBLIC ZLIB::ZLIB)
+else()
+ add_library(zlib_static STATIC IMPORTED)
+ set_target_properties(zlib_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/zlib/libz.a)
+ set_target_properties(zlib_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/zlib/include)
+ target_link_libraries(keeperfx PUBLIC zlib_static)
+ target_link_libraries(keeperfx_hvlog PUBLIC zlib_static)
+endif()
# Add minizip
-add_library(minizip_static STATIC IMPORTED)
-set_target_properties(minizip_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/zlib/libminizip.a)
-set_target_properties(minizip_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/zlib/include)
-target_link_libraries(minizip_static INTERFACE zlib_static)
-target_link_libraries(keeperfx PUBLIC minizip_static)
-target_link_libraries(keeperfx_hvlog PUBLIC minizip_static)
+if(MSVC)
+ find_package(unofficial-minizip CONFIG REQUIRED)
+ target_link_libraries(keeperfx PUBLIC unofficial::minizip::minizip)
+ target_link_libraries(keeperfx_hvlog PUBLIC unofficial::minizip::minizip)
+else()
+ add_library(minizip_static STATIC IMPORTED)
+ set_target_properties(minizip_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/zlib/libminizip.a)
+ set_target_properties(minizip_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/zlib/include)
+ target_link_libraries(minizip_static INTERFACE zlib_static)
+ target_link_libraries(keeperfx PUBLIC minizip_static)
+ target_link_libraries(keeperfx_hvlog PUBLIC minizip_static)
+endif()
## centitoml.
add_library(centitoml OBJECT "centitoml/toml_api.c")
-target_link_libraries(centitoml PUBLIC centijson_static)
+if(MSVC)
+ target_link_libraries(centitoml PUBLIC centijson::centijson)
+else()
+ target_link_libraries(centitoml PUBLIC centijson_static)
+endif()
target_include_directories(centitoml INTERFACE "centitoml")
target_link_libraries(keeperfx PUBLIC centitoml)
target_link_libraries(keeperfx_hvlog PUBLIC centitoml)
## ffmpeg
-add_library(libavcodec_static STATIC IMPORTED)
-set_target_properties(libavcodec_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/libavcodec/libavcodec.a)
-set_target_properties(libavcodec_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg)
+if(MSVC)
+ find_package(FFMPEG COMPONENTS AVCODEC AVFORMAT AVUTIL SWRESAMPLE REQUIRED)
+ target_include_directories(keeperfx PUBLIC ${FFMPEG_INCLUDE_DIRS})
+ target_include_directories(keeperfx_hvlog PUBLIC ${FFMPEG_INCLUDE_DIRS})
+ target_link_directories(keeperfx PUBLIC ${FFMPEG_LIBRARY_DIRS})
+ target_link_directories(keeperfx_hvlog PUBLIC ${FFMPEG_LIBRARY_DIRS})
+ target_link_libraries(keeperfx PUBLIC ${FFMPEG_LIBRARIES} bcrypt)
+ target_link_libraries(keeperfx_hvlog PUBLIC ${FFMPEG_LIBRARIES} bcrypt)
+else()
+ add_library(libavcodec_static STATIC IMPORTED)
+ set_target_properties(libavcodec_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/libavcodec/libavcodec.a)
+ set_target_properties(libavcodec_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg)
-add_library(libavformat_static STATIC IMPORTED)
-set_target_properties(libavformat_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/libavformat/libavformat.a)
-set_target_properties(libavformat_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg)
+ add_library(libavformat_static STATIC IMPORTED)
+ set_target_properties(libavformat_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/libavformat/libavformat.a)
+ set_target_properties(libavformat_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg)
-add_library(libavutil_static STATIC IMPORTED)
-set_target_properties(libavutil_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/libavutil/libavutil.a)
-set_target_properties(libavutil_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg)
+ add_library(libavutil_static STATIC IMPORTED)
+ set_target_properties(libavutil_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/libavutil/libavutil.a)
+ set_target_properties(libavutil_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg)
-add_library(libswresample_static STATIC IMPORTED)
-set_target_properties(libswresample_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/libswresample/libswresample.a)
-set_target_properties(libswresample_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg)
+ add_library(libswresample_static STATIC IMPORTED)
+ set_target_properties(libswresample_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/libswresample/libswresample.a)
+ set_target_properties(libswresample_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg)
-target_link_libraries(keeperfx PUBLIC bcrypt libavcodec_static libavformat_static libavutil_static libswresample_static)
-target_link_libraries(keeperfx_hvlog PUBLIC bcrypt libavcodec_static libavformat_static libavutil_static libswresample_static)
+ target_link_libraries(keeperfx PUBLIC libavformat_static libavcodec_static libswresample_static libavutil_static bcrypt)
+ target_link_libraries(keeperfx_hvlog PUBLIC libavformat_static libavcodec_static libswresample_static libavutil_static bcrypt)
+endif()
+
+## luajit
+if(MSVC)
+ find_library(LUAJIT_LIB NAMES lua51 REQUIRED)
+ find_path(LUAJIT_INCLUDE NAMES lua.h PATH_SUFFIXES luajit REQUIRED)
+ target_link_libraries(keeperfx PUBLIC ${LUAJIT_LIB})
+ target_link_libraries(keeperfx_hvlog PUBLIC ${LUAJIT_LIB})
+ target_include_directories(keeperfx PUBLIC ${LUAJIT_INCLUDE})
+ target_include_directories(keeperfx_hvlog PUBLIC ${LUAJIT_INCLUDE})
+else()
+ add_library(luajit_static STATIC IMPORTED)
+ set_target_properties(luajit_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/luajit/lib/libluajit.a)
+ set_target_properties(luajit_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/luajit/include)
+ target_link_libraries(keeperfx PUBLIC luajit_static)
+ target_link_libraries(keeperfx_hvlog PUBLIC luajit_static)
+endif()
+
+## openal
+if(MSVC)
+ find_package(OpenAL CONFIG REQUIRED)
+ target_link_libraries(keeperfx PUBLIC OpenAL::OpenAL)
+ target_link_libraries(keeperfx_hvlog PUBLIC OpenAL::OpenAL)
+else()
+ add_library(openal_static STATIC IMPORTED)
+ set_target_properties(openal_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/openal/libOpenAL32.a)
+ set_target_properties(openal_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/openal/include)
+ set_target_properties(openal_static PROPERTIES INTERFACE_COMPILE_DEFINITIONS "AL_LIBTYPE_STATIC=1")
+ target_link_libraries(openal_static INTERFACE avrt)
+ target_link_libraries(keeperfx PUBLIC openal_static)
+ target_link_libraries(keeperfx_hvlog PUBLIC openal_static)
+endif()
+
+## miniupnpc
+if(MSVC)
+ find_package(miniupnpc CONFIG REQUIRED)
+ target_link_libraries(keeperfx PUBLIC miniupnpc::miniupnpc)
+ target_link_libraries(keeperfx_hvlog PUBLIC miniupnpc::miniupnpc)
+else()
+ add_library(miniupnpc_static STATIC IMPORTED)
+ set_target_properties(miniupnpc_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/miniupnpc/libminiupnpc.a)
+ set_target_properties(miniupnpc_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/miniupnpc/include)
+ target_link_libraries(keeperfx PUBLIC miniupnpc_static)
+ target_link_libraries(keeperfx_hvlog PUBLIC miniupnpc_static)
+endif()
+
+## libnatpmp
+if(MSVC)
+ find_package(libnatpmp CONFIG REQUIRED)
+ target_link_libraries(keeperfx PUBLIC libnatpmp::libnatpmp)
+ target_link_libraries(keeperfx_hvlog PUBLIC libnatpmp::libnatpmp)
+else()
+ add_library(natpmp_static STATIC IMPORTED)
+ set_target_properties(natpmp_static PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libnatpmp/libnatpmp.a)
+ set_target_properties(natpmp_static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/libnatpmp/include)
+ target_link_libraries(natpmp_static INTERFACE iphlpapi)
+ target_link_libraries(keeperfx PUBLIC natpmp_static)
+ target_link_libraries(keeperfx_hvlog PUBLIC natpmp_static)
+endif()
diff --git a/external/vcpkg b/external/vcpkg
new file mode 160000
index 0000000000..4b77da7fed
--- /dev/null
+++ b/external/vcpkg
@@ -0,0 +1 @@
+Subproject commit 4b77da7fed37817f124936239197833469f1b9a8
diff --git a/src/compiler_compat.h b/src/compiler_compat.h
new file mode 100644
index 0000000000..e9dc6fbb5c
--- /dev/null
+++ b/src/compiler_compat.h
@@ -0,0 +1,223 @@
+/**
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+ * compiler_compat.h — GCC to MSVC attribute compatibility
+ * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+ *
+ * Maps GCC-specific function attributes (__attribute__) to MSVC/Clang equivalents
+ * or provides no-op fallbacks. Allows single-source compatibility across compilers.
+ *
+ * Include this header at the top of any .h/.hpp file using __attribute__.
+ */
+
+#ifndef COMPILER_COMPAT_H
+#define COMPILER_COMPAT_H
+
+// ━━━ Compiler Detection ━━━
+#if defined(_MSC_VER)
+ // MSVC (cl.exe)
+ #define KFX_COMPILER_MSVC 1
+ // MSVC does not support __attribute__ at all — define it away so existing
+ // code using raw __attribute__((nonnull)), __attribute__((format)) etc. compiles.
+ #ifndef __attribute__
+ #define __attribute__(x)
+ #endif
+ // __builtin_offsetof is a GCC extension; map it to standard offsetof().
+ // offsetof() is a compile-time constant on MSVC (unlike address arithmetic).
+ #include
+ #ifndef __builtin_offsetof
+ #define __builtin_offsetof(type, member) offsetof(type, member)
+ #endif
+#elif defined(__GNUC__)
+ // GCC or Clang (defines __GNUC__ for compatibility)
+ #define KFX_COMPILER_GCC 1
+#else
+ #define KFX_COMPILER_UNKNOWN 1
+#endif
+
+// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+// GCC Attribute Wrappers
+// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+/**
+ * KFX_NONNULL(indices...)
+ * Marks specific parameters as never NULL. GCC warns at compile-time if NULL is passed.
+ * MSVC doesn't have direct equivalent, but can use _Pragma disable for SAL warnings.
+ *
+ * Usage:
+ * int str_len(const char *str) KFX_NONNULL(1);
+ * int str_compare(const char *a, const char *b) KFX_NONNULL(1, 2);
+ */
+#if defined(KFX_COMPILER_GCC)
+ #define KFX_NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
+#elif defined(KFX_COMPILER_MSVC)
+ // MSVC: suppress /analyze warnings about potential NULL parameters if desired
+ // Most code doesn't use /analyze, so nonnull is a no-op here
+ #define KFX_NONNULL(...)
+#else
+ #define KFX_NONNULL(...)
+#endif
+
+/**
+ * KFX_PRINTF_FORMAT(fmt_idx, va_idx)
+ * Validates printf-style format string at index fmt_idx, with varargs starting at va_idx.
+ * Enables -Wformat warnings in GCC/Clang; MSVC has no direct equivalent.
+ *
+ * Usage:
+ * int my_printf(const char *fmt, ...) KFX_PRINTF_FORMAT(1, 2);
+ * int my_snprintf(char *buf, int size, const char *fmt, ...) KFX_PRINTF_FORMAT(3, 4);
+ */
+#if defined(KFX_COMPILER_GCC)
+ #define KFX_PRINTF_FORMAT(fmt_idx, va_idx) __attribute__((format(printf, fmt_idx, va_idx)))
+#elif defined(KFX_COMPILER_MSVC)
+ // MSVC: no direct equivalent. SAL annotations (e.g., _Printf_format_string_) exist
+ // but require Windows SDK headers and are less portable. Skip for now.
+ #define KFX_PRINTF_FORMAT(fmt_idx, va_idx)
+#else
+ #define KFX_PRINTF_FORMAT(fmt_idx, va_idx)
+#endif
+
+/**
+ * KFX_UNUSED
+ * Marks a variable/parameter as intentionally unused. Suppresses -Wunused warnings.
+ * C++17 standardizes this as [[maybe_unused]]; we provide backward compat.
+ *
+ * Usage:
+ * void handler(int event KFX_UNUSED) { }
+ * KFX_UNUSED int debug_val = some_expensive_computation();
+ */
+#if __cplusplus >= 201703L || __STDC_VERSION__ >= 202303L
+ // C++17 or C23+: use standard attribute
+ #define KFX_UNUSED [[maybe_unused]]
+#elif defined(KFX_COMPILER_GCC)
+ #define KFX_UNUSED __attribute__((unused))
+#elif defined(KFX_COMPILER_MSVC)
+ // MSVC: __pragma to suppress C4100 (unreferenced formal parameter)
+ // Or just leave blank; MSVC warnings for unused params are less noisy than GCC.
+ #define KFX_UNUSED
+#else
+ #define KFX_UNUSED
+#endif
+
+/**
+ * KFX_DEPRECATED
+ * Marks a function/variable as deprecated. Emits warning if used.
+ * Useful for gradual API migration.
+ *
+ * Usage:
+ * KFX_DEPRECATED int old_api() { return 42; }
+ */
+#if defined(KFX_COMPILER_GCC)
+ #define KFX_DEPRECATED __attribute__((deprecated))
+#elif defined(KFX_COMPILER_MSVC)
+ #define KFX_DEPRECATED __declspec(deprecated)
+#else
+ #define KFX_DEPRECATED
+#endif
+
+/**
+ * KFX_LIKELY / KFX_UNLIKELY
+ * Branch prediction hints for performance-critical paths.
+ * GCC/Clang: __builtin_expect. MSVC: __assume or no-op (optimizer is smart enough).
+ *
+ * Usage:
+ * if (KFX_LIKELY(x > 0)) { }
+ * if (KFX_UNLIKELY(err)) { }
+ */
+#if defined(KFX_COMPILER_GCC)
+ #define KFX_LIKELY(x) __builtin_expect(!!(x), 1)
+ #define KFX_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+ // MSVC/others: no-op; modern optimizers don't need hints
+ #define KFX_LIKELY(x) (x)
+ #define KFX_UNLIKELY(x) (x)
+#endif
+
+/**
+ * KFX_INLINE / KFX_FORCE_INLINE
+ * Inlining hints. GCC/Clang: inline/always_inline. MSVC: inline/__forceinline.
+ *
+ * Usage:
+ * KFX_INLINE int min(int a, int b) { return a < b ? a : b; }
+ * KFX_FORCE_INLINE int fast_path() { }
+ */
+#if defined(KFX_COMPILER_MSVC)
+ #define KFX_INLINE inline
+ #define KFX_FORCE_INLINE __forceinline
+#elif defined(KFX_COMPILER_GCC)
+ #define KFX_INLINE inline
+ #define KFX_FORCE_INLINE inline __attribute__((always_inline))
+#else
+ #define KFX_INLINE inline
+ #define KFX_FORCE_INLINE inline
+#endif
+
+/**
+ * KFX_NORETURN
+ * Marks a function that never returns (e.g., exit, abort, infinite loop).
+ * Helps optimizer and static analysis tools.
+ *
+ * Usage:
+ * KFX_NORETURN void fatal_error(const char *msg);
+ */
+#if __cplusplus >= 201103L || __STDC_VERSION__ >= 201112L
+ // C++11 or C11+: use standard _Noreturn (deprecated in C23) or [[noreturn]]
+ #if __cplusplus >= 201703L
+ #define KFX_NORETURN [[noreturn]]
+ #else
+ #define KFX_NORETURN _Noreturn
+ #endif
+#elif defined(KFX_COMPILER_GCC)
+ #define KFX_NORETURN __attribute__((noreturn))
+#elif defined(KFX_COMPILER_MSVC)
+ #define KFX_NORETURN __declspec(noreturn)
+#else
+ #define KFX_NORETURN
+#endif
+
+// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+// Common Attribute Combinations
+// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+/**
+ * KFX_PRINTF
+ * Combines KFX_NONNULL + KFX_PRINTF_FORMAT for printf-like functions.
+ * Parameters are 1-indexed (1-based indexing as per GCC convention).
+ */
+#define KFX_PRINTF(fmt_idx, va_idx) KFX_NONNULL(fmt_idx) KFX_PRINTF_FORMAT(fmt_idx, va_idx)
+
+/**
+ * KFX_PURE
+ * Function has no side effects and returns same value for same inputs.
+ * Allows compiler to optimize aggressive caching.
+ *
+ * Usage:
+ * KFX_PURE int absolute_value(int x);
+ */
+#if defined(KFX_COMPILER_GCC)
+ #define KFX_PURE __attribute__((pure))
+#elif defined(KFX_COMPILER_MSVC)
+ // MSVC: no direct equivalent, but intrinsic functions behave this way
+ #define KFX_PURE
+#else
+ #define KFX_PURE
+#endif
+
+// ══════════════════════════════════════════════════════════════════════════════
+// POSIX Function Compatibility for MSVC
+// ══════════════════════════════════════════════════════════════════════════════
+#ifdef KFX_COMPILER_MSVC
+ #include // _getcwd, _mkdir
+ #include // _access
+
+ // MSVC uses underscore-prefixed versions of POSIX functions
+ #define access _access
+ #define getcwd _getcwd
+ #define mkdir(path, mode) _mkdir(path) // MSVC mkdir takes 1 arg
+
+ // File access mode constants
+ #ifndef F_OK
+ #define F_OK 0 // Test for file existence
+ #endif
+#endif
+
+#endif // COMPILER_COMPAT_H
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 4e894a27c6..d4bea9e4f3 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -1,33 +1,134 @@
cmake_minimum_required(VERSION 3.20)
+# Docker-first: tools are pre-installed at /usr/local/bin in KFX build images.
+# find_program sets these cache variables; download fallback only runs if not found.
+find_program(PNGTOICO_PATH png2ico)
+find_program(PNGTORAW_PATH pngpal2raw)
+find_program(PNGTOBSPAL_PATH png2bestpal)
+find_program(POTONGDAT_PATH po2ngdat)
+find_program(SNDBANKER_PATH sndbanker)
+find_program(RNC_PATH rnc)
+find_program(DERNC_PATH dernc)
+
+if( NOT PNGTOICO_PATH OR NOT PNGTORAW_PATH OR NOT PNGTOBSPAL_PATH OR
+ NOT POTONGDAT_PATH OR NOT SNDBANKER_PATH OR NOT RNC_PATH OR NOT DERNC_PATH )
+
+# Determine platform-specific download URLs
if( ${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows" )
set( PNGTOICO_DOWNLOAD https://github.com/dkfans/png2ico/releases/download/2024-10-28/png2ico-win-2024-10-28.zip )
- set( ZIP_FILE_NAME png2ico-win-2024-10-28.zip )
- set( EXE_FILE_NAME png2ico.exe)
+ set( PNGTOICO_FILE png2ico-win-2024-10-28.zip )
+ set( PNGTOICO_EXE png2ico.exe)
+
+ set( PNGTORAW_DOWNLOAD https://github.com/dkfans/pngpal2raw/releases/download/v1.0.2/pngpal2raw-1_0_2_35-devel-win.zip )
+ set( PNGTORAW_FILE pngpal2raw-1_0_2_35-devel-win.zip )
+ set( PNGTORAW_EXE pngpal2raw.exe )
+
+ set( PNGTOBSPAL_DOWNLOAD https://github.com/dkfans/png2bestpal/releases/download/v1.0.3/png2bestpal-1_0_3_21-devel-win.zip )
+ set( PNGTOBSPAL_FILE png2bestpal-1_0_3_21-devel-win.zip )
+ set( PNGTOBSPAL_EXE png2bestpal.exe )
+
+ set( POTONGDAT_DOWNLOAD https://github.com/dkfans/po2ngdat/releases/download/v1.0.2.31-c/po2ngdat-1_0_2_31-devel-win.zip )
+ set( POTONGDAT_FILE po2ngdat-1_0_2_31-devel-win.zip )
+ set( POTONGDAT_EXE po2ngdat.exe )
+
+ set( SNDBANKER_DOWNLOAD https://github.com/dkfans/sndbanker/releases/download/v1.0.1/sndbanker-1_0_1_13-devel-win.zip )
+ set( SNDBANKER_FILE sndbanker-1_0_1_13-devel-win.zip )
+ set( SNDBANKER_EXE sndbanker.exe )
+
+ set( RNCTOOLS_DOWNLOAD https://github.com/dkfans/rnctools/releases/download/v1.0.2/rnctools-1_0_2_5-devel-win.zip )
+ set( RNCTOOLS_FILE rnctools-1_0_2_5-devel-win.zip )
+ set( RNC_EXE rnc.exe )
+ set( DERNC_EXE dernc.exe )
+
elseif( ${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux" )
set( PNGTOICO_DOWNLOAD https://github.com/dkfans/png2ico/releases/download/2024-10-28/png2ico-lin-2024-10-28.tar.gz )
- set( ZIP_FILE_NAME png2ico-lin-2024-10-28.tar.gz )
- set( EXE_FILE_NAME png2ico)
+ set( PNGTOICO_FILE png2ico-lin-2024-10-28.tar.gz )
+ set( PNGTOICO_EXE png2ico)
+
+ set( PNGTORAW_DOWNLOAD https://github.com/dkfans/pngpal2raw/releases/download/v1.0.2/pngpal2raw-1_0_2_35-devel-lin.tar.gz )
+ set( PNGTORAW_FILE pngpal2raw-1_0_2_35-devel-lin.tar.gz )
+ set( PNGTORAW_EXE pngpal2raw )
+
+ set( PNGTOBSPAL_DOWNLOAD https://github.com/dkfans/png2bestpal/releases/download/v1.0.3/png2bestpal-1_0_3_21-devel-lin.tar.gz )
+ set( PNGTOBSPAL_FILE png2bestpal-1_0_3_21-devel-lin.tar.gz )
+ set( PNGTOBSPAL_EXE png2bestpal )
+
+ set( POTONGDAT_DOWNLOAD https://github.com/dkfans/po2ngdat/releases/download/v1.0.2.31-c/po2ngdat-1_0_2_31-devel-lin.tar.gz )
+ set( POTONGDAT_FILE po2ngdat-1_0_2_31-devel-lin.tar.gz )
+ set( POTONGDAT_EXE po2ngdat )
+
+ set( SNDBANKER_DOWNLOAD https://github.com/dkfans/sndbanker/releases/download/v1.0.1/sndbanker-1_0_1_13-devel-lin.tar.gz )
+ set( SNDBANKER_FILE sndbanker-1_0_1_13-devel-lin.tar.gz )
+ set( SNDBANKER_EXE sndbanker )
+
+ set( RNCTOOLS_DOWNLOAD https://github.com/dkfans/rnctools/releases/download/v1.0.2/rnctools-1_0_2_5-devel-lin.tar.gz )
+ set( RNCTOOLS_FILE rnctools-1_0_2_5-devel-lin.tar.gz )
+ set( RNC_EXE rnc )
+ set( DERNC_EXE dernc )
endif()
-set(DESTINATION_DIR ${CMAKE_SOURCE_DIR}/tools/png2ico/${CMAKE_HOST_SYSTEM_NAME})
+ # Helper: download and extract a tool into tools//bin/
+ macro(download_tool TOOL_NAME TOOL_DOWNLOAD TOOL_FILE TOOL_DIR TOOL_EXE)
+ if( NOT EXISTS ${CMAKE_SOURCE_DIR}/tools/${TOOL_DIR}/bin/${TOOL_EXE} )
+ file( MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/tools/${TOOL_DIR} )
+ file( MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/tools/${TOOL_DIR}/bin )
+ if( NOT EXISTS ${CMAKE_SOURCE_DIR}/tools/${TOOL_DIR}/${TOOL_FILE} )
+ message(STATUS "Downloading ${TOOL_NAME}...")
+ file( DOWNLOAD ${TOOL_DOWNLOAD} ${CMAKE_SOURCE_DIR}/tools/${TOOL_DIR}/${TOOL_FILE} SHOW_PROGRESS )
+ endif()
+ message(STATUS "Extracting ${TOOL_NAME}...")
+ execute_process( COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/tools/${TOOL_DIR}/${TOOL_FILE} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tools/${TOOL_DIR}/bin )
+ endif()
+ endmacro()
-if( NOT EXISTS ${DESTINATION_DIR} )
- file( MAKE_DIRECTORY ${DESTINATION_DIR} )
- if( NOT EXISTS ${DESTINATION_DIR}/ZIP_FILE_NAME )
- file( DOWNLOAD ${PNGTOICO_DOWNLOAD} ${DESTINATION_DIR}/${ZIP_FILE_NAME} SHOW_PROGRESS )
- endif()
+ if( NOT PNGTOICO_PATH )
+ download_tool("png2ico" "${PNGTOICO_DOWNLOAD}" "${PNGTOICO_FILE}" "png2ico" "${PNGTOICO_EXE}")
+ set( PNGTOICO_PATH ${CMAKE_SOURCE_DIR}/tools/png2ico/bin/${PNGTOICO_EXE} )
+ endif()
+ if( NOT PNGTORAW_PATH )
+ download_tool("pngpal2raw" "${PNGTORAW_DOWNLOAD}" "${PNGTORAW_FILE}" "pngpal2raw" "${PNGTORAW_EXE}")
+ set( PNGTORAW_PATH ${CMAKE_SOURCE_DIR}/tools/pngpal2raw/bin/${PNGTORAW_EXE} )
+ endif()
+ if( NOT PNGTOBSPAL_PATH )
+ download_tool("png2bestpal" "${PNGTOBSPAL_DOWNLOAD}" "${PNGTOBSPAL_FILE}" "png2bestpal" "${PNGTOBSPAL_EXE}")
+ set( PNGTOBSPAL_PATH ${CMAKE_SOURCE_DIR}/tools/png2bestpal/bin/${PNGTOBSPAL_EXE} )
+ endif()
+ if( NOT POTONGDAT_PATH )
+ download_tool("po2ngdat" "${POTONGDAT_DOWNLOAD}" "${POTONGDAT_FILE}" "po2ngdat" "${POTONGDAT_EXE}")
+ set( POTONGDAT_PATH ${CMAKE_SOURCE_DIR}/tools/po2ngdat/bin/${POTONGDAT_EXE} )
+ endif()
+ if( NOT SNDBANKER_PATH )
+ download_tool("sndbanker" "${SNDBANKER_DOWNLOAD}" "${SNDBANKER_FILE}" "sndbanker" "${SNDBANKER_EXE}")
+ set( SNDBANKER_PATH ${CMAKE_SOURCE_DIR}/tools/sndbanker/bin/${SNDBANKER_EXE} )
+ endif()
+ if( NOT RNC_PATH )
+ download_tool("rnctools (rnc)" "${RNCTOOLS_DOWNLOAD}" "${RNCTOOLS_FILE}" "rnctools" "${RNC_EXE}")
+ set( RNC_PATH ${CMAKE_SOURCE_DIR}/tools/rnctools/bin/${RNC_EXE} )
+ endif()
+ if( NOT DERNC_PATH )
+ download_tool("rnctools (dernc)" "${RNCTOOLS_DOWNLOAD}" "${RNCTOOLS_FILE}" "rnctools" "${DERNC_EXE}")
+ set( DERNC_PATH ${CMAKE_SOURCE_DIR}/tools/rnctools/bin/${DERNC_EXE} )
+ endif()
- execute_process( COMMAND ${CMAKE_COMMAND} -E tar xzf ${DESTINATION_DIR}/${ZIP_FILE_NAME} WORKING_DIRECTORY ${DESTINATION_DIR} )
-endif()
+endif() # NOT all tools found on PATH
+
+# Propagate resolved paths to parent scope
+set(PNGTOICO_PATH ${PNGTOICO_PATH} PARENT_SCOPE)
+set(PNGTORAW_PATH ${PNGTORAW_PATH} PARENT_SCOPE)
+set(PNGTOBSPAL_PATH ${PNGTOBSPAL_PATH} PARENT_SCOPE)
+set(POTONGDAT_PATH ${POTONGDAT_PATH} PARENT_SCOPE)
+set(SNDBANKER_PATH ${SNDBANKER_PATH} PARENT_SCOPE)
+set(RNC_PATH ${RNC_PATH} PARENT_SCOPE)
+set(DERNC_PATH ${DERNC_PATH} PARENT_SCOPE)
+# Generate keeperfx icon from PNGs
set( PNG_FILE_PREFIX "keeperfx_icon")
add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/res/keeperfx_icon.ico
- COMMAND ${DESTINATION_DIR}/${EXE_FILE_NAME} keeperfx_icon.ico ${PNG_FILE_PREFIX}512-24bpp.png ${PNG_FILE_PREFIX}256-24bpp.png ${PNG_FILE_PREFIX}128-24bpp.png
+ COMMAND ${PNGTOICO_PATH} keeperfx_icon.ico ${PNG_FILE_PREFIX}512-24bpp.png ${PNG_FILE_PREFIX}256-24bpp.png ${PNG_FILE_PREFIX}128-24bpp.png
--colors 256 ${PNG_FILE_PREFIX}128-08bpp.png ${PNG_FILE_PREFIX}064-08bpp.png ${PNG_FILE_PREFIX}048-08bpp.png
- --colors 16 ${PNG_FILE_PREFIX}032-08bpp.png ${PNG_FILE_PREFIX}016-08bpp.png
+ --colors 16 ${PNG_FILE_PREFIX}032-08bpp.png ${PNG_FILE_PREFIX}016-08bpp.png
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/res
VERBATIM
)
diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json
index 93db3b45c8..10d0196e72 100644
--- a/vcpkg-configuration.json
+++ b/vcpkg-configuration.json
@@ -1,7 +1,7 @@
{
"default-registry": {
"kind": "git",
- "baseline": "9558037875497b9db8cf38fcd7db68ec661bffe7",
+ "baseline": "4b77da7fed37817f124936239197833469f1b9a8",
"repository": "https://github.com/microsoft/vcpkg"
},
"registries": [
@@ -9,6 +9,12 @@
"kind": "artifact",
"location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip",
"name": "microsoft"
+ },
+ {
+ "kind": "git",
+ "repository": "https://github.com/cerwym/keeperfx-vcpkg-registry",
+ "baseline": "dcb6b8df162cf6925237292ab5471d4cc7b4987d",
+ "packages": ["astronomy", "centijson", "enet6", "libnatpmp"]
}
]
}
diff --git a/vcpkg.json b/vcpkg.json
index 4e939a8186..1eb1cf7f06 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -3,6 +3,20 @@
"sdl2",
"sdl2-image",
"sdl2-mixer",
- "sdl2-net"
+ "sdl2-net",
+ {
+ "name": "ffmpeg",
+ "features": ["avcodec", "avformat", "swresample"]
+ },
+ "openal-soft",
+ "enet6",
+ "astronomy",
+ "centijson",
+ "libnatpmp",
+ "libspng",
+ "zlib",
+ "minizip",
+ "luajit",
+ "miniupnpc"
]
}
From 2cd352f4724b01eb157ad3195cecb3e399b953e4 Mon Sep 17 00:00:00 2001
From: Cerwym <1760289+Cerwym@users.noreply.github.com>
Date: Mon, 23 Mar 2026 11:35:24 +0000
Subject: [PATCH 03/15] fix(msvc): Source compatibility fixes for MSVC compiler
Fixes C99/C11 and POSIX constructs that MSVC does not support:
- Replace designated initializers with positional/memset patterns
- Fix mixed declarations and code (C89 requirement for MSVC /TC)
- Add extern C guards for C headers included from C++
- Fix vector subscript out-of-bounds in spritesheet.cpp
- Guard platform-specific includes (unistd.h, sys/stat.h) with ifdefs
- Clean up bflib_basics.h macro definitions for MSVC compatibility
---
CMakeLists.txt | 14 +++
build/ver_defs.h.in | 1 +
src/bflib_basics.h | 48 +++++-----
src/bflib_cpu.c | 43 +++++----
src/bflib_crash.c | 1 -
src/bflib_fileio.c | 2 +-
src/bflib_fmvids.cpp | 2 +
src/bflib_math.c | 2 +-
src/cdrom.cpp | 8 ++
src/config.c | 6 +-
src/config.h | 32 ++++++-
src/config_compp.c | 147 +++++++++++++++--------------
src/config_compp.h | 4 +-
src/config_crtrstates.c | 40 ++++----
src/config_cubes.c | 32 ++++---
src/config_effects.c | 103 +++++---------------
src/config_lenses.c | 54 +++++++----
src/config_magic.c | 94 ++++++++++---------
src/config_objects.c | 110 ++++++++++++----------
src/config_rules.c | 202 +++++++++++++++++++++-------------------
src/config_terrain.c | 101 +++++++++++---------
src/config_trapdoor.c | 191 ++++++++++++++++++++-----------------
src/console_cmd.c | 2 +-
src/custom_sprites.c | 4 +
src/lua_api_things.c | 12 ++-
src/main.cpp | 4 +-
src/net_portforward.cpp | 8 ++
src/spritesheet.cpp | 14 ++-
28 files changed, 710 insertions(+), 571 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2602b6f7b0..5cc0929ddf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,6 +18,20 @@ set(VER_BUILD 0)
set(VER_STRING "${VER_MAJOR}.${VER_MINOR}.${VER_RELEASE}.${VER_BUILD} ${PACKAGE_SUFFIX}")
set(PACKAGE_SUFFIX "")
+# Extract git revision
+find_package(Git QUIET)
+if(GIT_FOUND)
+ execute_process(
+ COMMAND ${GIT_EXECUTABLE} describe --always
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_VARIABLE GIT_REVISION
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET
+ )
+else()
+ set(GIT_REVISION "unknown")
+endif()
+
# Generate version header
set(KEEPERFX_VER_DEFS_H_IN ${CMAKE_SOURCE_DIR}/build/ver_defs.h.in)
set(KEEPERFX_VER_DEFS_H_OUT ${CMAKE_SOURCE_DIR}/src/ver_defs.h)
diff --git a/build/ver_defs.h.in b/build/ver_defs.h.in
index 48b98d9f2c..f2b30fdcc6 100644
--- a/build/ver_defs.h.in
+++ b/build/ver_defs.h.in
@@ -4,4 +4,5 @@
#define VER_BUILD ${VER_BUILD}
#define VER_STRING "${VER_STRING}"
#define PACKAGE_SUFFIX "${PACKAGE_SUFFIX}"
+#define GIT_REVISION "${GIT_REVISION}"
diff --git a/src/bflib_basics.h b/src/bflib_basics.h
index f61ac2060d..9296f98513 100644
--- a/src/bflib_basics.h
+++ b/src/bflib_basics.h
@@ -24,6 +24,8 @@
#include
#include
+#include "compiler_compat.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -129,43 +131,43 @@ extern char consoleLogArray[MAX_CONSOLE_LOG_COUNT][MAX_TEXT_LENGTH];
extern size_t consoleLogArraySize;
// High level functions - DK specific
-short warning_dialog(const char *codefile,const int ecode,const char *message) __attribute__ ((nonnull(1, 3)));
-short error_dialog(const char *codefile,const int ecode,const char *message) __attribute__ ((nonnull(1, 3)));
-short error_dialog_fatal(const char *codefile,const int ecode,const char *message) __attribute__ ((nonnull(1, 3)));
-int str_append(char * buffer, int size, const char * str) __attribute__ ((nonnull(1, 3)));
-int str_appendf(char * buffer, int size, const char * format, ...) __attribute__ ((format(printf, 3, 4), nonnull(1, 3)));
+short warning_dialog(const char *codefile,const int ecode,const char *message) KFX_NONNULL(1, 3);
+short error_dialog(const char *codefile,const int ecode,const char *message) KFX_NONNULL(1, 3);
+short error_dialog_fatal(const char *codefile,const int ecode,const char *message) KFX_NONNULL(1, 3);
+int str_append(char * buffer, int size, const char * str) KFX_NONNULL(1, 3);
+int str_appendf(char * buffer, int size, const char * format, ...) KFX_PRINTF_FORMAT(3, 4) KFX_NONNULL(1, 3);
/******************************************************************************/
-int LbErrorLog(const char *format, ...) __attribute__ ((format(printf, 1, 2), nonnull(1)));
-int LbWarnLog(const char *format, ...) __attribute__ ((format(printf, 1, 2), nonnull(1)));
-int LbSyncLog(const char *format, ...) __attribute__ ((format(printf, 1, 2), nonnull(1)));
-int LbNetLog(const char *format, ...) __attribute__ ((format(printf, 1, 2), nonnull(1)));
-int LbJustLog(const char *format, ...) __attribute__ ((format(printf, 1, 2), nonnull(1)));
-int LbNaviLog(const char *format, ...) __attribute__ ((format(printf, 1, 2), nonnull(1)));
+int LbErrorLog(const char *format, ...) KFX_PRINTF_FORMAT(1, 2) KFX_NONNULL(1);
+int LbWarnLog(const char *format, ...) KFX_PRINTF_FORMAT(1, 2) KFX_NONNULL(1);
+int LbSyncLog(const char *format, ...) KFX_PRINTF_FORMAT(1, 2) KFX_NONNULL(1);
+int LbNetLog(const char *format, ...) KFX_PRINTF_FORMAT(1, 2) KFX_NONNULL(1);
+int LbJustLog(const char *format, ...) KFX_PRINTF_FORMAT(1, 2) KFX_NONNULL(1);
+int LbNaviLog(const char *format, ...) KFX_PRINTF_FORMAT(1, 2) KFX_NONNULL(1);
#ifdef FUNCTESTING
-int LbFTestLog(const char *format, ...) __attribute__ ((format(printf, 1, 2), nonnull(1)));
+int LbFTestLog(const char *format, ...) KFX_PRINTF_FORMAT(1, 2) KFX_NONNULL(1);
#endif
-int LbScriptLog(unsigned long line,const char *format, ...) __attribute__ ((format(printf, 2, 3), nonnull(2)));
-int LbConfigLog(unsigned long line,const char *format, ...) __attribute__ ((format(printf, 2, 3), nonnull(2)));
+int LbScriptLog(unsigned long line,const char *format, ...) KFX_PRINTF_FORMAT(2, 3) KFX_NONNULL(2);
+int LbConfigLog(unsigned long line,const char *format, ...) KFX_PRINTF_FORMAT(2, 3) KFX_NONNULL(2);
int LbErrorLogSetup(const char *directory, const char *filename, TbBool flag);
int LbErrorLogClose(void);
-int LbLogClose(struct TbLog *log) __attribute__ ((nonnull(1)));
-int LbLogSetup(struct TbLog *log, const char *filename, ulong flags) __attribute__ ((nonnull(1, 2)));
-int LbLogSetPrefix(struct TbLog *log, const char *prefix) __attribute__ ((nonnull(1, 2)));
-int LbLogSetPrefixFmt(struct TbLog *log, const char *format, ...) __attribute__ ((format(printf, 2, 3), nonnull(1, 2)));
+int LbLogClose(struct TbLog *log) KFX_NONNULL(1);
+int LbLogSetup(struct TbLog *log, const char *filename, ulong flags) KFX_NONNULL(1, 2);
+int LbLogSetPrefix(struct TbLog *log, const char *prefix) KFX_NONNULL(1, 2);
+int LbLogSetPrefixFmt(struct TbLog *log, const char *format, ...) KFX_PRINTF_FORMAT(2, 3) KFX_NONNULL(1, 2);
/******************************************************************************/
typedef void (*TbNetworkCallbackFunc)(struct TbNetworkCallbackData *, void *);
/******************************************************************************/
-unsigned long llong (unsigned char *p) __attribute__ ((nonnull(1)));
-unsigned long lword (unsigned char *p) __attribute__ ((nonnull(1)));
+unsigned long llong (unsigned char *p) KFX_NONNULL(1);
+unsigned long lword (unsigned char *p) KFX_NONNULL(1);
long saturate_set_signed(long long val,unsigned short nbits);
unsigned long saturate_set_unsigned(unsigned long long val,unsigned short nbits);
-void make_lowercase(char *) __attribute__ ((nonnull(1)));
-void make_uppercase(char *) __attribute__ ((nonnull(1)));
-int natoi(const char * str, int len) __attribute__ ((nonnull(1))); // like atoi but stops after len bytes
+void make_lowercase(char *) KFX_NONNULL(1);
+void make_uppercase(char *) KFX_NONNULL(1);
+int natoi(const char * str, int len) KFX_NONNULL(1); // like atoi but stops after len bytes
/**
* Converts an index number to a flag - by creating a bitmask where only the nth bit is set to 1.
diff --git a/src/bflib_cpu.c b/src/bflib_cpu.c
index 5c7c865809..a6b1ab3f83 100644
--- a/src/bflib_cpu.c
+++ b/src/bflib_cpu.c
@@ -17,37 +17,50 @@
* (at your option) any later version.
*/
/******************************************************************************/
+
#include "pre_inc.h"
#include "bflib_cpu.h"
#include "bflib_basics.h"
#include "post_inc.h"
+#ifdef _MSC_VER
+#include
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
/******************************************************************************/
-/** Issue a single request to CPUID.
- * Fits 'intel features', for instance note that even if only "eax" and "edx"
- * are of interest, other registers will be modified by the operation,
- * so we need to tell the compiler about it.
- */
+#ifdef _MSC_VER
+/** Issue a single request to CPUID (MSVC). */
+static inline void cpuid(int code, uint32_t *a, uint32_t *d) {
+ int info[4];
+ __cpuid(info, code);
+ *a = (uint32_t)info[0];
+ *d = (uint32_t)info[3];
+}
+
+/** Issue a complete request, storing general registers output in an array (MSVC). */
+static inline void cpuid_string(int code, void *destination) {
+ int info[4];
+ __cpuid(info, code);
+ memcpy(destination, info, 16);
+}
+#else
+/** Issue a single request to CPUID. */
static inline void cpuid(int code, uint32_t *a, uint32_t *d) {
- #if defined(__i386__) || defined(__x86_64__)
asm volatile("cpuid":"=a"(*a),"=d"(*d):"0"(code):"ecx","ebx");
- #endif
}
-/** Issue a complete request, storing general registers output in an array.
- */
+/** Issue a complete request, storing general registers output in an array. */
static inline void cpuid_string(int code, void * destination) {
- #if defined(__i386__) || defined(__x86_64__)
- uint32_t * where = (uint32_t *) destination;
- asm volatile("cpuid":"=a"(*where),"=b"(*(where+1)),
- "=c"(*(where+2)),"=d"(*(where+3)):"0"(code));
- #endif
+ uint32_t * where = (uint32_t *) destination;
+ asm volatile("cpuid":"=a"(*where),"=b"(*(where+1)),
+ "=c"(*(where+2)),"=d"(*(where+3)):"0"(code));
}
+#endif
/******************************************************************************/
void cpu_detect(struct CPU_INFO *cpu)
@@ -58,7 +71,6 @@ void cpu_detect(struct CPU_INFO *cpu)
cpu->timeStampCounter = 0;
cpu->feature_intl = 0;
cpu->feature_edx = 0;
- #if defined(__i386__) || defined(__x86_64__)
{
uint32_t where[4];
cpuid_string(CPUID_GETVENDORSTRING, where);
@@ -83,7 +95,6 @@ void cpu_detect(struct CPU_INFO *cpu)
cpuid_string(CPUID_INTELBRANDSTRINGEND, &cpu->brand[32]);
}
}
- #endif
}
unsigned char cpu_get_type(struct CPU_INFO *cpu)
diff --git a/src/bflib_crash.c b/src/bflib_crash.c
index 00545f3dae..1766063bd6 100644
--- a/src/bflib_crash.c
+++ b/src/bflib_crash.c
@@ -33,7 +33,6 @@
#define WIN32_LEAN_AND_MEAN
#include
#include
-#include
#include
#include
#endif
diff --git a/src/bflib_fileio.c b/src/bflib_fileio.c
index 03f3bd9771..dc26094a9d 100644
--- a/src/bflib_fileio.c
+++ b/src/bflib_fileio.c
@@ -145,7 +145,7 @@ int create_directory_for_file(const char * fname)
memcpy(tmp, fname, separator - fname);
tmp[separator - fname] = 0;
#if defined(_WIN32)
- if (mkdir(tmp) != 0) {
+ if (_mkdir(tmp) != 0) {
#else
if (mkdir(tmp, 0755) != 0) {
#endif
diff --git a/src/bflib_fmvids.cpp b/src/bflib_fmvids.cpp
index cc58370486..b963d72021 100644
--- a/src/bflib_fmvids.cpp
+++ b/src/bflib_fmvids.cpp
@@ -13,7 +13,9 @@ extern "C" {
#include
#include
#include
+ #ifdef __GNUC__
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
+ #endif
}
#include
diff --git a/src/bflib_math.c b/src/bflib_math.c
index 2aff3d1bca..f606b278cc 100644
--- a/src/bflib_math.c
+++ b/src/bflib_math.c
@@ -675,7 +675,7 @@ static long bitScanReverse(long s)
{
unsigned long source = (unsigned long)s;
#if defined(_MSC_VER)
- DWORD i;
+ unsigned long i;
uint8_t success = _BitScanReverse(&i, source);
return success != 0 ? i : -1;
#elif defined(__GNUC__)
diff --git a/src/cdrom.cpp b/src/cdrom.cpp
index 5664f67b1d..292d34018a 100644
--- a/src/cdrom.cpp
+++ b/src/cdrom.cpp
@@ -1,3 +1,11 @@
+#ifdef _MSC_VER
+// MSVC: include windows.h before project headers to guarantee correct OLE/MSG
+// type ordering. Other project headers may pull in windows.h with WIN32_LEAN_AND_MEAN
+// which excludes winuser.h/ole types; going first prevents that race.
+#include
+#include
+#endif
+
#include "pre_inc.h"
#include "bflib_sndlib.h"
#include "game_legacy.h"
diff --git a/src/config.c b/src/config.c
index 467dcc9f6d..6c93f392e6 100644
--- a/src/config.c
+++ b/src/config.c
@@ -980,7 +980,7 @@ TbBool parse_named_field_block(const char *buf, long len, const char *config_tex
void set_defaults(const struct NamedFieldSet* named_fields_set, const char *config_textname)
{
- memset((void *)named_fields_set->struct_base, 0, named_fields_set->struct_size * named_fields_set->max_count);
+ memset((void *)named_fields_set->get_struct_base(), 0, named_fields_set->struct_size * named_fields_set->max_count);
const struct NamedField* name_NamedField = NULL;
@@ -1037,8 +1037,8 @@ TbBool parse_named_field_blocks(char *buf, long len, const char *config_textname
const int i = natoi(&blockname[basename_len], blocknamelen - basename_len);
if (i < 0 || i >= named_fields_set->max_count) {
continue;
- } else if (i >= *named_fields_set->count_field) {
- *named_fields_set->count_field = i + 1;
+ } else if (i >= *named_fields_set->get_count()) {
+ *named_fields_set->get_count() = i + 1;
}
char blockname_null[COMMAND_WORD_LEN];
strncpy(blockname_null, blockname, blocknamelen);
diff --git a/src/config.h b/src/config.h
index 1d3c09d328..f0ab85bdae 100644
--- a/src/config.h
+++ b/src/config.h
@@ -175,8 +175,27 @@ enum dataTypes
char: dt_char, \
default: dt_default)))
-#define field(field)\
- &field, var_type(field)
+// field_t: portable, works on all C99+ compilers including MSVC.
+// Takes the struct type name explicitly — produces a compile-time constant offset.
+// Use for simple (non-array-subscript) member paths.
+#include
+#define field_t(type_name, member_path) \
+ (void*)(ptrdiff_t)offsetof(type_name, member_path), \
+ var_type(((type_name*)0)->member_path)
+
+// field_a: like field_t but for array-element member paths array[idx].
+// offsetof(T, arr) + idx*sizeof(element) is compile-time constant on all compilers,
+// whereas offsetof(T, arr[n]) is a GCC extension rejected by MSVC.
+#define field_a(type_name, array_member, idx) \
+ (void*)(ptrdiff_t)(offsetof(type_name, array_member) + (idx) * sizeof(((type_name*)0)->array_member[0])), \
+ var_type(((type_name*)0)->array_member[idx])
+
+// field: GCC/Clang-only convenience alias that infers the type from an expression
+// using the typeof extension. Do not use in new code — prefer field_t()/field_a().
+#ifndef _MSC_VER
+#define field(elem0_expr, member_path) \
+ field_t(typeof(elem0_expr), member_path)
+#endif
/******************************************************************************/
struct CommandWord {
@@ -209,13 +228,13 @@ struct NamedField {
};
struct NamedFieldSet {
- int32_t *const count_field;
+ int32_t* (*get_count)(void);
const char* block_basename;
const struct NamedField* named_fields;
struct NamedCommand* names;
const int max_count;
const size_t struct_size;
- const void* struct_base;
+ void* (*get_struct_base)(void);
};
#define NAMFIELDWRNLOG(format, ...) LbWarnLog("%s(line %lu): " format "\n", src_str , text_line_number, ##__VA_ARGS__)
@@ -246,6 +265,11 @@ char *prepare_file_fmtpath_mod(const char *mod_dir, short fgroup, const char *fm
char *prepare_file_path_buf(char *dst, int dst_size, short fgroup, const char *fname);
char *prepare_file_path(short fgroup, const char *fname);
char *prepare_file_fmtpath(short fgroup, const char *fmt_str, ...);
+/* New API - self-documenting game vs. mod distinction */
+char *get_game_file_path(short fgroup, const char *fname);
+char *get_mod_file_path(const char *mod_dir, short fgroup, const char *fname);
+char *get_game_file_path_fmt(short fgroup, const char *fmt_str, ...);
+char *get_mod_file_path_fmt(const char *mod_dir, short fgroup, const char *fmt_str, ...);
unsigned char *load_data_file_to_buffer(int32_t *ldsize, short fgroup, const char *fmt_str, ...);
/******************************************************************************/
TbBool load_config(const struct ConfigFileData* file_data, unsigned short flags);
diff --git a/src/config_compp.c b/src/config_compp.c
index 77501f5115..bb429a9f01 100644
--- a/src/config_compp.c
+++ b/src/config_compp.c
@@ -16,6 +16,7 @@
* (at your option) any later version.
*/
/******************************************************************************/
+
#include "pre_inc.h"
#include "config_compp.h"
#include "globals.h"
@@ -67,16 +68,26 @@ static int computer_type_add_event(struct ComputerType *cpt, unsigned char event
/******************************************************************************/
static const struct NamedField compp_common_named_fields[] = {
- {"ComputerAssists", 0, field(comp_player_conf.computer_assist_types[0]), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
- {"ComputerAssists", 1, field(comp_player_conf.computer_assist_types[1]), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
- {"ComputerAssists", 2, field(comp_player_conf.computer_assist_types[2]), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
- {"ComputerAssists", 3, field(comp_player_conf.computer_assist_types[3]), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
- {"SkirmishFirst", 0, field(comp_player_conf.skirmish_first), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
- {"SkirmishLast", 0, field(comp_player_conf.skirmish_last), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
- {"DefaultComputerAssist", 0, field(comp_player_conf.player_assist_default), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
+ {"ComputerAssists", 0, field_a(struct ComputerPlayerConfig, computer_assist_types, 0), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
+ {"ComputerAssists", 1, field_a(struct ComputerPlayerConfig, computer_assist_types, 1), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
+ {"ComputerAssists", 2, field_a(struct ComputerPlayerConfig, computer_assist_types, 2), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
+ {"ComputerAssists", 3, field_a(struct ComputerPlayerConfig, computer_assist_types, 3), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
+ {"SkirmishFirst", 0, field_t(struct ComputerPlayerConfig, skirmish_first), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
+ {"SkirmishLast", 0, field_t(struct ComputerPlayerConfig, skirmish_last), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
+ {"DefaultComputerAssist", 0, field_t(struct ComputerPlayerConfig, player_assist_default), 0, 0,COMPUTER_MODELS_COUNT, NULL, value_default, assign_default},
{NULL},
};
+static void* get_compp_common_base(void) { return &comp_player_conf; }
+static int32_t* get_processes_count(void) { return &comp_player_conf.processes_count; }
+static void* get_processes_base(void) { return comp_player_conf.process_types; }
+static int32_t* get_checks_count(void) { return &comp_player_conf.checks_count; }
+static void* get_checks_base(void) { return comp_player_conf.check_types; }
+static int32_t* get_events_count(void) { return &comp_player_conf.events_count; }
+static void* get_events_base(void) { return comp_player_conf.event_types; }
+static int32_t* get_computers_count(void) { return &comp_player_conf.computers_count; }
+static void* get_computers_base(void) { return comp_player_conf.computer_types; }
+
const struct NamedFieldSet compp_common_named_fields_set = {
NULL,
"common",
@@ -84,124 +95,124 @@ const struct NamedFieldSet compp_common_named_fields_set = {
NULL,
0,
0,
- NULL,
+ get_compp_common_base,
};
static const struct NamedField compp_process_named_fields[] = {
//name //pos //field //default //min //max //NamedCommand
- {"NAME", -1, field(comp_player_conf.process_types[0].name), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
- {"MNEMONIC", 0, field(comp_player_conf.process_types[0].mnemonic), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
- {"VALUES", 0, field(comp_player_conf.process_types[0].priority ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 1, field(comp_player_conf.process_types[0].process_configuration_value_2 ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 2, field(comp_player_conf.process_types[0].process_configuration_value_3 ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 3, field(comp_player_conf.process_types[0].process_configuration_value_4 ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 4, field(comp_player_conf.process_types[0].process_configuration_value_5 ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"FUNCTIONS", 0, field(comp_player_conf.process_types[0].func_check ), 0, INT32_MIN,UINT32_MAX, computer_process_func_type, value_default, assign_default},
- {"FUNCTIONS", 1, field(comp_player_conf.process_types[0].func_setup ), 0, INT32_MIN,UINT32_MAX, computer_process_func_type, value_default, assign_default},
- {"FUNCTIONS", 2, field(comp_player_conf.process_types[0].func_task ), 0, INT32_MIN,UINT32_MAX, computer_process_func_type, value_default, assign_default},
- {"FUNCTIONS", 3, field(comp_player_conf.process_types[0].func_complete), 0, INT32_MIN,UINT32_MAX, computer_process_func_type, value_default, assign_default},
- {"FUNCTIONS", 4, field(comp_player_conf.process_types[0].func_pause ), 0, INT32_MIN,UINT32_MAX, computer_process_func_type, value_default, assign_default},
- {"PARAMS", 0, field(comp_player_conf.process_types[0].process_parameter_1 ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PARAMS", 1, field(comp_player_conf.process_types[0].process_parameter_2 ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PARAMS", 2, field(comp_player_conf.process_types[0].process_parameter_3 ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PARAMS", 3, field(comp_player_conf.process_types[0].last_run_turn), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PARAMS", 4, field(comp_player_conf.process_types[0].process_parameter_5 ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PARAMS", 5, field(comp_player_conf.process_types[0].flags ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"NAME", -1, field_t(struct ComputerProcess, name), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
+ {"MNEMONIC", 0, field_t(struct ComputerProcess, mnemonic), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
+ {"VALUES", 0, field_t(struct ComputerProcess, priority), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 1, field_t(struct ComputerProcess, process_configuration_value_2), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 2, field_t(struct ComputerProcess, process_configuration_value_3), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 3, field_t(struct ComputerProcess, process_configuration_value_4), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 4, field_t(struct ComputerProcess, process_configuration_value_5), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"FUNCTIONS", 0, field_t(struct ComputerProcess, func_check), 0, INT32_MIN,UINT32_MAX, computer_process_func_type, value_default, assign_default},
+ {"FUNCTIONS", 1, field_t(struct ComputerProcess, func_setup), 0, INT32_MIN,UINT32_MAX, computer_process_func_type, value_default, assign_default},
+ {"FUNCTIONS", 2, field_t(struct ComputerProcess, func_task), 0, INT32_MIN,UINT32_MAX, computer_process_func_type, value_default, assign_default},
+ {"FUNCTIONS", 3, field_t(struct ComputerProcess, func_complete), 0, INT32_MIN,UINT32_MAX, computer_process_func_type, value_default, assign_default},
+ {"FUNCTIONS", 4, field_t(struct ComputerProcess, func_pause), 0, INT32_MIN,UINT32_MAX, computer_process_func_type, value_default, assign_default},
+ {"PARAMS", 0, field_t(struct ComputerProcess, process_parameter_1), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PARAMS", 1, field_t(struct ComputerProcess, process_parameter_2), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PARAMS", 2, field_t(struct ComputerProcess, process_parameter_3), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PARAMS", 3, field_t(struct ComputerProcess, last_run_turn), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PARAMS", 4, field_t(struct ComputerProcess, process_parameter_5), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PARAMS", 5, field_t(struct ComputerProcess, flags), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
{NULL},
};
const struct NamedFieldSet compp_process_named_fields_set = {
- &comp_player_conf.processes_count,
+ get_processes_count,
"process",
compp_process_named_fields,
NULL,
COMPUTER_PROCESS_TYPES_COUNT,
sizeof(comp_player_conf.process_types[0]),
- comp_player_conf.process_types,
+ get_processes_base,
};
static const struct NamedField compp_check_named_fields[] = {
//name //pos //field //default //min //max //NamedCommand
- {"NAME", -1, field(comp_player_conf.check_types[0].name ), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
- {"MNEMONIC", 0, field(comp_player_conf.check_types[0].mnemonic ), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
- {"VALUES", 0, field(comp_player_conf.check_types[0].flags ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 1, field(comp_player_conf.check_types[0].turns_interval), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"FUNCTIONS", 0, field(comp_player_conf.check_types[0].func ), 0, INT32_MIN,UINT32_MAX, computer_check_func_type, value_default, assign_default},
- {"PARAMS", 0, field(comp_player_conf.check_types[0].primary_parameter ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PARAMS", 1, field(comp_player_conf.check_types[0].secondary_parameter ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PARAMS", 2, field(comp_player_conf.check_types[0].tertiary_parameter ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PARAMS", 3, field(comp_player_conf.check_types[0].last_run_turn ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"NAME", -1, field_t(struct ComputerCheck, name), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
+ {"MNEMONIC", 0, field_t(struct ComputerCheck, mnemonic), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
+ {"VALUES", 0, field_t(struct ComputerCheck, flags), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 1, field_t(struct ComputerCheck, turns_interval), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"FUNCTIONS", 0, field_t(struct ComputerCheck, func), 0, INT32_MIN,UINT32_MAX, computer_check_func_type, value_default, assign_default},
+ {"PARAMS", 0, field_t(struct ComputerCheck, primary_parameter), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PARAMS", 1, field_t(struct ComputerCheck, secondary_parameter), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PARAMS", 2, field_t(struct ComputerCheck, tertiary_parameter), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PARAMS", 3, field_t(struct ComputerCheck, last_run_turn), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
{NULL},
};
const struct NamedFieldSet compp_check_named_fields_set = {
- &comp_player_conf.checks_count,
+ get_checks_count,
"check",
compp_check_named_fields,
NULL,
COMPUTER_CHECKS_TYPES_COUNT,
sizeof(comp_player_conf.check_types[0]),
- comp_player_conf.check_types,
+ get_checks_base,
};
static const struct NamedField compp_event_named_fields[] = {
//name //pos //field //default //min //max //NamedCommand
- {"NAME", -1, field(comp_player_conf.event_types[0].name ), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
- {"MNEMONIC", 0, field(comp_player_conf.event_types[0].mnemonic ), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
- {"VALUES", 0, field(comp_player_conf.event_types[0].cetype ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 1, field(comp_player_conf.event_types[0].mevent_kind ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 2, field(comp_player_conf.event_types[0].test_interval ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"FUNCTIONS", 0, field(comp_player_conf.event_types[0].func_event ), 0, INT32_MIN,UINT32_MAX, computer_event_func_type, value_default, assign_default},
- {"FUNCTIONS", 0, field(comp_player_conf.event_types[0].func_test ), 0, INT32_MIN,UINT32_MAX, computer_event_test_func_type, value_default, assign_default},
- {"PROCESS", 0, field(comp_player_conf.event_types[0].process ), 0, INT32_MIN,UINT32_MAX, NULL, value_process_mnemonic, assign_default},
- {"PARAMS", 0, field(comp_player_conf.event_types[0].primary_parameter ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PARAMS", 1, field(comp_player_conf.event_types[0].secondary_parameter ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PARAMS", 2, field(comp_player_conf.event_types[0].tertiary_parameter ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PARAMS", 3, field(comp_player_conf.event_types[0].last_test_gameturn ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"NAME", -1, field_t(struct ComputerEvent, name), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
+ {"MNEMONIC", 0, field_t(struct ComputerEvent, mnemonic), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
+ {"VALUES", 0, field_t(struct ComputerEvent, cetype), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 1, field_t(struct ComputerEvent, mevent_kind), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 2, field_t(struct ComputerEvent, test_interval), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"FUNCTIONS", 0, field_t(struct ComputerEvent, func_event), 0, INT32_MIN,UINT32_MAX, computer_event_func_type, value_default, assign_default},
+ {"FUNCTIONS", 0, field_t(struct ComputerEvent, func_test), 0, INT32_MIN,UINT32_MAX, computer_event_test_func_type, value_default, assign_default},
+ {"PROCESS", 0, field_t(struct ComputerEvent, process), 0, INT32_MIN,UINT32_MAX, NULL, value_process_mnemonic, assign_default},
+ {"PARAMS", 0, field_t(struct ComputerEvent, primary_parameter), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PARAMS", 1, field_t(struct ComputerEvent, secondary_parameter), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PARAMS", 2, field_t(struct ComputerEvent, tertiary_parameter), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PARAMS", 3, field_t(struct ComputerEvent, last_test_gameturn), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
{NULL},
};
const struct NamedFieldSet compp_event_named_fields_set = {
- &comp_player_conf.events_count,
+ get_events_count,
"event",
compp_event_named_fields,
NULL,
COMPUTER_EVENTS_TYPES_COUNT,
sizeof(comp_player_conf.event_types[0]),
- comp_player_conf.event_types,
+ get_events_base,
};
static const struct NamedField compp_computer_named_fields[] = {
//name //pos //field //default //min //max //NamedCommand
- {"NAME", -1, field(comp_player_conf.computer_types[0].name), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
- {"TOOLTIPTEXTID", 0, field(comp_player_conf.computer_types[0].tooltip_stridx),GUIStr_Empty, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"ASSISTANTICON", 0, field(comp_player_conf.computer_types[0].sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
- {"VALUES", 0, field(comp_player_conf.computer_types[0].dig_stack_size ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 1, field(comp_player_conf.computer_types[0].processes_time ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 2, field(comp_player_conf.computer_types[0].click_rate), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 3, field(comp_player_conf.computer_types[0].max_room_build_tasks), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 4, field(comp_player_conf.computer_types[0].turn_begin ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 5, field(comp_player_conf.computer_types[0].sim_before_dig ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"VALUES", 5, field(comp_player_conf.computer_types[0].drop_delay ), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PROCESSES", -1, field(comp_player_conf.computer_types[0].processes ), 0, INT32_MIN,UINT32_MAX, NULL, value_processes, assign_null},
- {"CHECKS", -1, field(comp_player_conf.computer_types[0].checks ), 0, INT32_MIN,UINT32_MAX, NULL, value_checks, assign_null},
- {"EVENTS", -1, field(comp_player_conf.computer_types[0].events ), 0, INT32_MIN,UINT32_MAX, NULL, value_events, assign_null},
+ {"NAME", -1, field_t(struct ComputerType, name), 0, INT32_MIN,UINT32_MAX, NULL, value_name, assign_null},
+ {"TOOLTIPTEXTID", 0, field_t(struct ComputerType, tooltip_stridx),GUIStr_Empty, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"ASSISTANTICON", 0, field_t(struct ComputerType, sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
+ {"VALUES", 0, field_t(struct ComputerType, dig_stack_size), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 1, field_t(struct ComputerType, processes_time), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 2, field_t(struct ComputerType, click_rate), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 3, field_t(struct ComputerType, max_room_build_tasks), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 4, field_t(struct ComputerType, turn_begin), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 5, field_t(struct ComputerType, sim_before_dig), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"VALUES", 5, field_t(struct ComputerType, drop_delay), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PROCESSES", -1, field_t(struct ComputerType, processes), 0, INT32_MIN,UINT32_MAX, NULL, value_processes, assign_null},
+ {"CHECKS", -1, field_t(struct ComputerType, checks), 0, INT32_MIN,UINT32_MAX, NULL, value_checks, assign_null},
+ {"EVENTS", -1, field_t(struct ComputerType, events), 0, INT32_MIN,UINT32_MAX, NULL, value_events, assign_null},
{NULL},
};
const struct NamedFieldSet compp_computer_named_fields_set = {
- &comp_player_conf.computers_count,
+ get_computers_count,
"computer",
compp_computer_named_fields,
NULL,
COMPUTER_MODELS_COUNT,
sizeof(comp_player_conf.computer_types[0]),
- comp_player_conf.computer_types,
+ get_computers_base,
};
/******************************************************************************/
diff --git a/src/config_compp.h b/src/config_compp.h
index ac2fef9c8b..f9660697ba 100644
--- a/src/config_compp.h
+++ b/src/config_compp.h
@@ -132,10 +132,10 @@ extern const struct ConfigFileData keeper_keepcomp_file_data;
/******************************************************************************/
struct ComputerType *get_computer_type_template(long cpt_idx);
/******************************************************************************/
+extern struct ComputerPlayerConfig comp_player_conf;
+/******************************************************************************/
#ifdef __cplusplus
}
#endif
-extern struct ComputerPlayerConfig comp_player_conf;
-
#endif
diff --git a/src/config_crtrstates.c b/src/config_crtrstates.c
index f6a4770c47..a2d1af3b9b 100644
--- a/src/config_crtrstates.c
+++ b/src/config_crtrstates.c
@@ -16,6 +16,7 @@
* (at your option) any later version.
*/
/******************************************************************************/
+
#include "pre_inc.h"
#include "config_crtrstates.h"
#include "globals.h"
@@ -115,33 +116,40 @@ int64_t value_overrides(const struct NamedField* named_field, const char* value_
return 0;
}
+#pragma push_macro("game")
+#undef game
const struct NamedField crstates_states_named_fields[] = {
- {"NAME", 0, field(game.conf.crtr_conf.states[0].name), 0, 0, 0, creatrstate_desc, value_name, assign_null},
- {"PROCESSFUNCTION", 0, field(game.conf.crtr_conf.states[0].process_state), 0, 0, 0, process_func_commands, value_function, assign_default},
- {"CLEANUPFUNCTION", 0, field(game.conf.crtr_conf.states[0].cleanup_state), 0, 0, 0, cleanup_func_commands, value_function, assign_default},
- {"MOVEFROMSLABFUNCTION", 0, field(game.conf.crtr_conf.states[0].move_from_slab), 0, 0, 0, move_from_slab_func_commands, value_function, assign_default},
- {"MOVECHECKFUNCTION", 0, field(game.conf.crtr_conf.states[0].move_check), 0, 0, 0, move_check_func_commands, value_function, assign_default},
+ {"NAME", 0, field_t(struct CreatureStateConfig, name), 0, 0, 0, creatrstate_desc, value_name, assign_null},
+ {"PROCESSFUNCTION", 0, field_t(struct CreatureStateConfig, process_state), 0, 0, 0, process_func_commands, value_function, assign_default},
+ {"CLEANUPFUNCTION", 0, field_t(struct CreatureStateConfig, cleanup_state), 0, 0, 0, cleanup_func_commands, value_function, assign_default},
+ {"MOVEFROMSLABFUNCTION", 0, field_t(struct CreatureStateConfig, move_from_slab), 0, 0, 0, move_from_slab_func_commands, value_function, assign_default},
+ {"MOVECHECKFUNCTION", 0, field_t(struct CreatureStateConfig, move_check), 0, 0, 0, move_check_func_commands, value_function, assign_default},
{"OVERRIDES", -1, NULL,0, 0, 0, 0, NULL, value_overrides, assign_null},
- {"STATETYPE", 0, field(game.conf.crtr_conf.states[0].state_type), 0, 0, 0, creature_state_types_commands, value_default, assign_default},
- {"CAPTIVE", 0, field(game.conf.crtr_conf.states[0].captive), 0, 0, 1, NULL, value_default, assign_default},
- {"TRANSITION", 0, field(game.conf.crtr_conf.states[0].transition), 0, 0, 1, NULL, value_default, assign_default},
- {"FOLLOWBEHAVIOR", 0, field(game.conf.crtr_conf.states[0].follow_behavior), 0, 0, 0, follow_behavior_commands, value_default, assign_default},
- {"BLOCKSALLSTATECHANGES", 0, field(game.conf.crtr_conf.states[0].blocks_all_state_changes), 0, 0, 1, NULL, value_default, assign_default},
- {"SPRITEIDX", 0, field(game.conf.crtr_conf.states[0].sprite_idx), 0, 0, 0, NULL, value_icon, assign_icon},
- {"DISPLAYTHOUGHTBUBBLE", 0, field(game.conf.crtr_conf.states[0].display_thought_bubble), 0, 0, 1, NULL, value_default, assign_default},
- {"SNEAKY", 0, field(game.conf.crtr_conf.states[0].sneaky), 0, 0, 1, NULL, value_default, assign_default},
- {"REACTTOCTA", 0, field(game.conf.crtr_conf.states[0].react_to_cta), 0, 0, 1, NULL, value_default, assign_default},
+ {"STATETYPE", 0, field_t(struct CreatureStateConfig, state_type), 0, 0, 0, creature_state_types_commands, value_default, assign_default},
+ {"CAPTIVE", 0, field_t(struct CreatureStateConfig, captive), 0, 0, 1, NULL, value_default, assign_default},
+ {"TRANSITION", 0, field_t(struct CreatureStateConfig, transition), 0, 0, 1, NULL, value_default, assign_default},
+ {"FOLLOWBEHAVIOR", 0, field_t(struct CreatureStateConfig, follow_behavior), 0, 0, 0, follow_behavior_commands, value_default, assign_default},
+ {"BLOCKSALLSTATECHANGES", 0, field_t(struct CreatureStateConfig, blocks_all_state_changes), 0, 0, 1, NULL, value_default, assign_default},
+ {"SPRITEIDX", 0, field_t(struct CreatureStateConfig, sprite_idx), 0, 0, 0, NULL, value_icon, assign_icon},
+ {"DISPLAYTHOUGHTBUBBLE", 0, field_t(struct CreatureStateConfig, display_thought_bubble), 0, 0, 1, NULL, value_default, assign_default},
+ {"SNEAKY", 0, field_t(struct CreatureStateConfig, sneaky), 0, 0, 1, NULL, value_default, assign_default},
+ {"REACTTOCTA", 0, field_t(struct CreatureStateConfig, react_to_cta), 0, 0, 1, NULL, value_default, assign_default},
{NULL},
};
+#pragma pop_macro("game")
+
+static int32_t* get_crstates_count(void) { return &game.conf.crtr_conf.states_count; }
+static void* get_crstates_base(void) { return game.conf.crtr_conf.states; }
+
const struct NamedFieldSet crstates_states_named_fields_set = {
- &game.conf.crtr_conf.states_count,
+ get_crstates_count,
"state",
crstates_states_named_fields,
creatrstate_desc,
CREATURE_STATES_MAX,
sizeof(game.conf.crtr_conf.states[0]),
- game.conf.crtr_conf.states,
+ get_crstates_base,
};
static TbBool load_creaturestates_config_file(const char *fname, unsigned short flags);
diff --git a/src/config_cubes.c b/src/config_cubes.c
index 7801a3a589..593334d827 100644
--- a/src/config_cubes.c
+++ b/src/config_cubes.c
@@ -16,6 +16,7 @@
* (at your option) any later version.
*/
/******************************************************************************/
+
#include "pre_inc.h"
#include "bflib_basics.h"
#include "bflib_fileio.h"
@@ -51,29 +52,36 @@ static const struct NamedCommand cubes_properties_flags[] = {
{NULL, 0},
};
+#pragma push_macro("game")
+#undef game
static const struct NamedField cubes_named_fields[] = {
//name //pos //field //default //min //max //NamedCommand
- {"Name", 0, field(game.conf.cube_conf.cube_cfgstats[0].code_name), 0, 0, 0, cube_desc, value_name, assign_null},
- {"Textures", 0, field(game.conf.cube_conf.cube_cfgstats[0].texture_id[0]), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
- {"Textures", 1, field(game.conf.cube_conf.cube_cfgstats[0].texture_id[1]), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
- {"Textures", 2, field(game.conf.cube_conf.cube_cfgstats[0].texture_id[2]), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
- {"Textures", 3, field(game.conf.cube_conf.cube_cfgstats[0].texture_id[3]), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
- {"Textures", 4, field(game.conf.cube_conf.cube_cfgstats[0].texture_id[4]), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
- {"Textures", 5, field(game.conf.cube_conf.cube_cfgstats[0].texture_id[5]), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
- {"OwnershipGroup", 0, field(game.conf.cube_conf.cube_cfgstats[0].ownershipGroup), 0, 0, CUBE_OWNERSHIP_GROUPS, NULL, value_default, assign_default},
- {"Owner", 0, field(game.conf.cube_conf.cube_cfgstats[0].owner), 0, 0, PLAYERS_COUNT, cmpgn_human_player_options,value_default, assign_owner},
- {"Properties", -1, field(game.conf.cube_conf.cube_cfgstats[0].properties_flags), 0, 0, UCHAR_MAX, cubes_properties_flags, value_flagsfield,assign_default},
+ {"Name", 0, field_t(struct CubeConfigStats, code_name), 0, 0, 0, cube_desc, value_name, assign_null},
+ {"Textures", 0, field_a(struct CubeConfigStats, texture_id, 0), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
+ {"Textures", 1, field_a(struct CubeConfigStats, texture_id, 1), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
+ {"Textures", 2, field_a(struct CubeConfigStats, texture_id, 2), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
+ {"Textures", 3, field_a(struct CubeConfigStats, texture_id, 3), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
+ {"Textures", 4, field_a(struct CubeConfigStats, texture_id, 4), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
+ {"Textures", 5, field_a(struct CubeConfigStats, texture_id, 5), 0, 0, USHRT_MAX, NULL, value_default, assign_default},
+ {"OwnershipGroup", 0, field_t(struct CubeConfigStats, ownershipGroup), 0, 0, CUBE_OWNERSHIP_GROUPS, NULL, value_default, assign_default},
+ {"Owner", 0, field_t(struct CubeConfigStats, owner), 0, 0, PLAYERS_COUNT, cmpgn_human_player_options,value_default, assign_owner},
+ {"Properties", -1, field_t(struct CubeConfigStats, properties_flags), 0, 0, UCHAR_MAX, cubes_properties_flags, value_flagsfield,assign_default},
{NULL},
};
+#pragma pop_macro("game")
+
+static int32_t* get_cubes_count(void) { return &game.conf.cube_conf.cube_types_count; }
+static void* get_cubes_base(void) { return game.conf.cube_conf.cube_cfgstats; }
+
const struct NamedFieldSet cubes_named_fields_set = {
- &game.conf.cube_conf.cube_types_count,
+ get_cubes_count,
"cube",
cubes_named_fields,
cube_desc,
CUBE_ITEMS_MAX,
sizeof(game.conf.cube_conf.cube_cfgstats[0]),
- game.conf.cube_conf.cube_cfgstats,
+ get_cubes_base,
};
diff --git a/src/config_effects.c b/src/config_effects.c
index 466d7cfd85..560561b923 100644
--- a/src/config_effects.c
+++ b/src/config_effects.c
@@ -16,6 +16,7 @@
* (at your option) any later version.
*/
/******************************************************************************/
+
#include "pre_inc.h"
#include "config_effects.h"
#include "globals.h"
@@ -47,34 +48,41 @@ const struct ConfigFileData keeper_effects_file_data = {
};
+#pragma push_macro("game")
+#undef game
const struct NamedField effects_effectgenerator_named_fields[] = {
- {"NAME", 0, field(game.conf.effects_conf.effectgen_cfgstats[0].code_name), 0, INT32_MIN, UINT32_MAX, effectgen_desc, value_name, assign_null},
- {"GENERATIONDELAYMIN", 0, field(game.conf.effects_conf.effectgen_cfgstats[0].generation_delay_min), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"GENERATIONDELAYMAX", 0, field(game.conf.effects_conf.effectgen_cfgstats[0].generation_delay_max), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"GENERATIONAMOUNT", 0, field(game.conf.effects_conf.effectgen_cfgstats[0].generation_amount), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"EFFECTMODEL", 0, field(game.conf.effects_conf.effectgen_cfgstats[0].effect_model), 0, INT32_MIN, UINT32_MAX, NULL, value_effOrEffEl,assign_default},
- {"IGNORETERRAIN", 0, field(game.conf.effects_conf.effectgen_cfgstats[0].ignore_terrain), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"SPAWNHEIGHT", 0, field(game.conf.effects_conf.effectgen_cfgstats[0].spawn_height), 1, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"ACCELERATIONMIN", 0, field(game.conf.effects_conf.effectgen_cfgstats[0].acc_x_min), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"ACCELERATIONMIN", 1, field(game.conf.effects_conf.effectgen_cfgstats[0].acc_y_min), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"ACCELERATIONMIN", 2, field(game.conf.effects_conf.effectgen_cfgstats[0].acc_z_min), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"ACCELERATIONMAX", 0, field(game.conf.effects_conf.effectgen_cfgstats[0].acc_x_max), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"ACCELERATIONMAX", 1, field(game.conf.effects_conf.effectgen_cfgstats[0].acc_y_max), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"ACCELERATIONMAX", 2, field(game.conf.effects_conf.effectgen_cfgstats[0].acc_z_max), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"SOUND", 0, field(game.conf.effects_conf.effectgen_cfgstats[0].sound_sample_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"SOUND", 1, field(game.conf.effects_conf.effectgen_cfgstats[0].sound_sample_rng), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"NAME", 0, field_t(struct EffectGeneratorConfigStats, code_name), 0, INT32_MIN, UINT32_MAX, effectgen_desc, value_name, assign_null},
+ {"GENERATIONDELAYMIN", 0, field_t(struct EffectGeneratorConfigStats, generation_delay_min), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"GENERATIONDELAYMAX", 0, field_t(struct EffectGeneratorConfigStats, generation_delay_max), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"GENERATIONAMOUNT", 0, field_t(struct EffectGeneratorConfigStats, generation_amount), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"EFFECTMODEL", 0, field_t(struct EffectGeneratorConfigStats, effect_model), 0, INT32_MIN, UINT32_MAX, NULL, value_effOrEffEl,assign_default},
+ {"IGNORETERRAIN", 0, field_t(struct EffectGeneratorConfigStats, ignore_terrain), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"SPAWNHEIGHT", 0, field_t(struct EffectGeneratorConfigStats, spawn_height), 1, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"ACCELERATIONMIN", 0, field_t(struct EffectGeneratorConfigStats, acc_x_min), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"ACCELERATIONMIN", 1, field_t(struct EffectGeneratorConfigStats, acc_y_min), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"ACCELERATIONMIN", 2, field_t(struct EffectGeneratorConfigStats, acc_z_min), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"ACCELERATIONMAX", 0, field_t(struct EffectGeneratorConfigStats, acc_x_max), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"ACCELERATIONMAX", 1, field_t(struct EffectGeneratorConfigStats, acc_y_max), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"ACCELERATIONMAX", 2, field_t(struct EffectGeneratorConfigStats, acc_z_max), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"SOUND", 0, field_t(struct EffectGeneratorConfigStats, sound_sample_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"SOUND", 1, field_t(struct EffectGeneratorConfigStats, sound_sample_rng), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
{NULL},
};
+#pragma pop_macro("game")
+
+static int32_t* get_effectgen_count(void) { return &game.conf.effects_conf.effectgen_cfgstats_count; }
+static void* get_effectgen_base(void) { return game.conf.effects_conf.effectgen_cfgstats; }
+
const struct NamedFieldSet effects_effectgenerator_named_fields_set = {
- &game.conf.effects_conf.effectgen_cfgstats_count,
+ get_effectgen_count,
"effectGenerator",
effects_effectgenerator_named_fields,
effectgen_desc,
EFFECTSGEN_TYPES_MAX,
sizeof(game.conf.effects_conf.effectgen_cfgstats[0]),
- game.conf.effects_conf.effectgen_cfgstats,
+ get_effectgen_base,
};
int32_t const imp_spangle_effects[] = {
@@ -97,7 +105,6 @@ static void load_effects(VALUE *value, unsigned short flags)
{
char key[64] = "";
VALUE *section;
- int max_effect_id = -1;
for (int id = 0; id < EFFECTS_TYPES_MAX; id++)
{
{
@@ -109,7 +116,6 @@ static void load_effects(VALUE *value, unsigned short flags)
struct EffectConfigStats *effcst = &game.conf.effects_conf.effect_cfgstats[id];
SET_NAME(section,effect_desc,effcst->code_name);
- max_effect_id = id;
CONDITIONAL_ASSIGN_ARR2_INT_MINMAX(section,"GenerationAccelXYRange",effcst->accel_xy_min,effcst->accel_xy_max);
CONDITIONAL_ASSIGN_ARR2_INT_MINMAX(section,"GenerationAccelZRange", effcst->accel_z_min, effcst->accel_z_max);
@@ -128,32 +134,12 @@ static void load_effects(VALUE *value, unsigned short flags)
CONDITIONAL_ASSIGN_SPELL(section,"SpellEffect",effcst->spell_effect);
}
}
-
- // Set sentinel NULL entry to mark the end of valid entries
- if (max_effect_id >= 0)
- {
- if (max_effect_id + 1 < EFFECTS_TYPES_MAX)
- {
- effect_desc[max_effect_id + 1].name = NULL;
- }
- // Fill any gaps with placeholder entries so get_id() won't terminate early
- for (int id = 0; id <= max_effect_id; id++)
- {
- if (effect_desc[id].name == NULL)
- {
- effect_desc[id].num = id;
- // Use the code_name from the effect config (should be initialized to empty or "NULL")
- effect_desc[id].name = game.conf.effects_conf.effect_cfgstats[id].code_name;
- }
- }
- }
}
static void load_effectsgenerators(VALUE *value, unsigned short flags)
{
char key[KEY_SIZE];
VALUE *section;
- int max_effectgen_id = -1;
for (int id = 0; id < EFFECTSGEN_TYPES_MAX; id++)
{
{
@@ -165,7 +151,6 @@ static void load_effectsgenerators(VALUE *value, unsigned short flags)
struct EffectGeneratorConfigStats *effgencst = &game.conf.effects_conf.effectgen_cfgstats[id];
SET_NAME(section,effectgen_desc,effgencst->code_name);
- max_effectgen_id = id;
CONDITIONAL_ASSIGN_INT(section,"GenerationDelayMin",effgencst->generation_delay_min);
CONDITIONAL_ASSIGN_INT(section,"GenerationDelayMax",effgencst->generation_delay_max);
@@ -180,31 +165,12 @@ static void load_effectsgenerators(VALUE *value, unsigned short flags)
CONDITIONAL_ASSIGN_ARR2_INT(section,"Sound",effgencst->sound_sample_idx,effgencst->sound_sample_rng);
}
}
- // Set sentinel NULL entry to mark the end of valid entries
- if (max_effectgen_id >= 0)
- {
- if (max_effectgen_id + 1 < EFFECTSGEN_TYPES_MAX)
- {
- effectgen_desc[max_effectgen_id + 1].name = NULL;
- }
- // Fill any gaps with placeholder entries so get_id() won't terminate early
- for (int id = 0; id <= max_effectgen_id; id++)
- {
- if (effectgen_desc[id].name == NULL)
- {
- effectgen_desc[id].num = id;
- // Use the code_name from the effect generator config (should be initialized to empty or "NULL")
- effectgen_desc[id].name = game.conf.effects_conf.effectgen_cfgstats[id].code_name;
- }
- }
- }
}
static void load_effectelements(VALUE *value, unsigned short flags)
{
char key[KEY_SIZE];
VALUE *section;
- int max_effectelement_id = -1;
for (int id = 0; id < EFFECTSELLEMENTS_TYPES_MAX; id++)
{
{
@@ -216,7 +182,6 @@ static void load_effectelements(VALUE *value, unsigned short flags)
struct EffectElementConfigStats *effelcst = &game.conf.effects_conf.effectelement_cfgstats[id];
SET_NAME(section,effectelem_desc,effelcst->code_name);
- max_effectelement_id = id;
CONDITIONAL_ASSIGN_INT(section,"DrawClass", effelcst->draw_class);
CONDITIONAL_ASSIGN_INT(section,"MoveType", effelcst->move_type);
@@ -263,24 +228,6 @@ static void load_effectelements(VALUE *value, unsigned short flags)
CONDITIONAL_ASSIGN_INT(section,"AffectedByWind", effelcst->affected_by_wind );
}
}
- // Set sentinel NULL entry to mark the end of valid entries
- if (max_effectelement_id >= 0)
- {
- if (max_effectelement_id + 1 < EFFECTSELLEMENTS_TYPES_MAX)
- {
- effectelem_desc[max_effectelement_id + 1].name = NULL;
- }
- // Fill any gaps with placeholder entries so get_id() won't terminate early
- for (int id = 0; id <= max_effectelement_id; id++)
- {
- if (effectelem_desc[id].name == NULL)
- {
- effectelem_desc[id].num = id;
- // Use the code_name from the effect element config (should be initialized to empty or "NULL")
- effectelem_desc[id].name = game.conf.effects_conf.effectelement_cfgstats[id].code_name;
- }
- }
- }
}
static TbBool load_effects_config_file(const char *fname, unsigned short flags)
diff --git a/src/config_lenses.c b/src/config_lenses.c
index c99e2a2c23..71d2b8a63c 100644
--- a/src/config_lenses.c
+++ b/src/config_lenses.c
@@ -16,6 +16,7 @@
* (at your option) any later version.
*/
/******************************************************************************/
+
#include "pre_inc.h"
#include "config_lenses.h"
#include "globals.h"
@@ -54,31 +55,34 @@ static int64_t value_overlay(const struct NamedField* named_field, const char* v
const struct NamedField lenses_data_named_fields[] = {
//name //pos //field //default //min //max //NamedCommand
- {"NAME", 0, field(lenses_conf.lenses[0].code_name), 0, 0, 0, lenses_desc, value_name, assign_null},
- {"MIST", 0, field(lenses_conf.lenses[0].mist_file), 0, 0, 0, NULL, value_mist, assign_null},
- {"MIST", 1, field(lenses_conf.lenses[0].mist_lightness), 0, 0, 63, NULL, value_default, assign_default},
- {"MIST", 2, field(lenses_conf.lenses[0].mist_ghost), 0, 0, 255, NULL, value_default, assign_default},
- {"MIST", 3, field(lenses_conf.lenses[0].mist_pos_x_step), 2, 0, 255, NULL, value_default, assign_default},
- {"MIST", 4, field(lenses_conf.lenses[0].mist_pos_y_step), 1, 0, 255, NULL, value_default, assign_default},
- {"MIST", 5, field(lenses_conf.lenses[0].mist_sec_x_step), 253, 0, 255, NULL, value_default, assign_default},
- {"MIST", 6, field(lenses_conf.lenses[0].mist_sec_y_step), 3, 0, 255, NULL, value_default, assign_default},
- {"DISPLACEMENT", 0, field(lenses_conf.lenses[0].displace_kind), 0, 0, 255, NULL, value_default, assign_default},
- {"DISPLACEMENT", 1, field(lenses_conf.lenses[0].displace_magnitude), 0, 0, 511, NULL, value_default, assign_default},
- {"DISPLACEMENT", 2, field(lenses_conf.lenses[0].displace_period), 1, 0, 511, NULL, value_displace, assign_default},
- {"PALETTE", 0, field(lenses_conf.lenses[0].palette), 0, 0, 0, NULL, value_pallete, assign_null},
- {"OVERLAY", 0, field(lenses_conf.lenses[0].overlay_file), 0, 0, 0, NULL, value_overlay, assign_null},
- {"OVERLAY", 1, field(lenses_conf.lenses[0].overlay_alpha), 128, 0, 255, NULL, value_default, assign_default},
+ {"NAME", 0, field_t(struct LensConfig, code_name), 0, 0, 0, lenses_desc, value_name, assign_null},
+ {"MIST", 0, field_t(struct LensConfig, mist_file), 0, 0, 0, NULL, value_mist, assign_null},
+ {"MIST", 1, field_t(struct LensConfig, mist_lightness), 0, 0, 63, NULL, value_default, assign_default},
+ {"MIST", 2, field_t(struct LensConfig, mist_ghost), 0, 0, 255, NULL, value_default, assign_default},
+ {"MIST", 3, field_t(struct LensConfig, mist_pos_x_step), 2, 0, 255, NULL, value_default, assign_default},
+ {"MIST", 4, field_t(struct LensConfig, mist_pos_y_step), 1, 0, 255, NULL, value_default, assign_default},
+ {"MIST", 5, field_t(struct LensConfig, mist_sec_x_step), 253, 0, 255, NULL, value_default, assign_default},
+ {"MIST", 6, field_t(struct LensConfig, mist_sec_y_step), 3, 0, 255, NULL, value_default, assign_default},
+ {"DISPLACEMENT", 0, field_t(struct LensConfig, displace_kind), 0, 0, 255, NULL, value_default, assign_default},
+ {"DISPLACEMENT", 1, field_t(struct LensConfig, displace_magnitude), 0, 0, 511, NULL, value_default, assign_default},
+ {"DISPLACEMENT", 2, field_t(struct LensConfig, displace_period), 1, 0, 511, NULL, value_displace, assign_default},
+ {"PALETTE", 0, field_t(struct LensConfig, palette), 0, 0, 0, NULL, value_pallete, assign_null},
+ {"OVERLAY", 0, field_t(struct LensConfig, overlay_file), 0, 0, 0, NULL, value_overlay, assign_null},
+ {"OVERLAY", 1, field_t(struct LensConfig, overlay_alpha), 128, 0, 255, NULL, value_default, assign_default},
{NULL},
};
+static int32_t* get_lenses_count(void) { return &lenses_conf.lenses_count; }
+static void* get_lenses_base(void) { return lenses_conf.lenses; }
+
const struct NamedFieldSet lenses_data_named_fields_set = {
- &lenses_conf.lenses_count,
+ get_lenses_count,
"lens",
lenses_data_named_fields,
lenses_desc,
LENS_ITEMS_MAX,
sizeof(lenses_conf.lenses[0]),
- lenses_conf.lenses,
+ get_lenses_base,
};
/******************************************************************************/
@@ -92,12 +96,20 @@ struct LensConfig *get_lens_config(long lens_idx)
static int64_t value_mist(const struct NamedField* named_field, const char* value_text, const struct NamedFieldSet* named_fields_set, int idx, const char* src_str, unsigned char flags)
{
+ if (idx < 0 || idx >= named_fields_set->max_count) {
+ ERRORMSG("Config index %d out of bounds [0,%d) for mist in lens.cfg", idx, named_fields_set->max_count);
+ return 0;
+ }
lenses_conf.lenses[idx].flags |= LCF_HasMist;
return value_name(named_field, value_text, named_fields_set, idx, src_str, flags);
}
static int64_t value_displace(const struct NamedField* named_field, const char* value_text, const struct NamedFieldSet* named_fields_set, int idx, const char* src_str, unsigned char flags)
{
+ if (idx < 0 || idx >= named_fields_set->max_count) {
+ ERRORMSG("Config index %d out of bounds [0,%d) for displace in lens.cfg", idx, named_fields_set->max_count);
+ return 0;
+ }
lenses_conf.lenses[idx].flags |= LCF_HasDisplace;
return value_default(named_field, value_text, named_fields_set, idx, src_str, flags);
}
@@ -105,9 +117,13 @@ static int64_t value_displace(const struct NamedField* named_field, const char*
static int64_t value_pallete(const struct NamedField* named_field, const char* value_text, const struct NamedFieldSet* named_fields_set, int idx, const char* src_str, unsigned char flags)
{
+ if (idx < 0 || idx >= named_fields_set->max_count) {
+ ERRORMSG("Config index %d out of bounds [0,%d) for palette in lens.cfg", idx, named_fields_set->max_count);
+ return 0;
+ }
lenses_conf.lenses[idx].flags |= LCF_HasPalette;
char* fname = prepare_file_path(FGrp_StdData, value_text);
- if (LbFileLoadAt(fname, (char*)(named_field->field) + named_fields_set->struct_size * idx) != PALETTE_SIZE)
+ if (LbFileLoadAt(fname, (char*)named_fields_set->get_struct_base() + named_fields_set->struct_size * idx + (ptrdiff_t)named_field->field) != PALETTE_SIZE)
{
CONFWRNLOG("Couldn't load \"%s\" file for \"%s\" parameter in [%s%d] block of lens.cfg file.",
value_text, named_field->name, named_fields_set->block_basename, idx);
@@ -117,6 +133,10 @@ static int64_t value_pallete(const struct NamedField* named_field, const char* v
static int64_t value_overlay(const struct NamedField* named_field, const char* value_text, const struct NamedFieldSet* named_fields_set, int idx, const char* src_str, unsigned char flags)
{
+ if (idx < 0 || idx >= named_fields_set->max_count) {
+ ERRORMSG("Config index %d out of bounds [0,%d) for overlay in lens.cfg", idx, named_fields_set->max_count);
+ return 0;
+ }
SYNCDBG (9, "value_overlay called: argnum=%d, value='%s', lens=%d", named_field->argnum, value_text, idx);
if (value_text == NULL || value_text[0] == '\0') {
diff --git a/src/config_magic.c b/src/config_magic.c
index e32bb1176e..0616cbdc6d 100644
--- a/src/config_magic.c
+++ b/src/config_magic.c
@@ -16,6 +16,7 @@
* (at your option) any later version.
*/
/******************************************************************************/
+
#include "pre_inc.h"
#include "config_magic.h"
#include "globals.h"
@@ -363,60 +364,67 @@ static void assign_strength_before_last(const struct NamedField* named_field, in
assign_default(named_field,value,named_fields_set,idx,src_str,flags);
}
+#pragma push_macro("game")
+#undef game
static const struct NamedField magic_powers_named_fields[] = {
//name //pos //field //default //min //max //NamedCommand
- {"NAME", 0, field(game.conf.magic_conf.power_cfgstats[0].code_name), 0, INT32_MIN,UINT32_MAX, power_desc, value_name, assign_null},
- {"POWER", 0, field(game.conf.magic_conf.power_cfgstats[0].strength[0]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"POWER", 1, field(game.conf.magic_conf.power_cfgstats[0].strength[1]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"POWER", 2, field(game.conf.magic_conf.power_cfgstats[0].strength[2]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"POWER", 3, field(game.conf.magic_conf.power_cfgstats[0].strength[3]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"POWER", 4, field(game.conf.magic_conf.power_cfgstats[0].strength[4]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"POWER", 5, field(game.conf.magic_conf.power_cfgstats[0].strength[5]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"POWER", 6, field(game.conf.magic_conf.power_cfgstats[0].strength[6]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"POWER", 7, field(game.conf.magic_conf.power_cfgstats[0].strength[7]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"POWER", 8, field(game.conf.magic_conf.power_cfgstats[0].strength[8]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_strength_before_last},
- {"POWER", 8, field(game.conf.magic_conf.power_cfgstats[0].strength[9]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"COST", 0, field(game.conf.magic_conf.power_cfgstats[0].cost[0]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"COST", 1, field(game.conf.magic_conf.power_cfgstats[0].cost[1]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"COST", 2, field(game.conf.magic_conf.power_cfgstats[0].cost[2]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"COST", 3, field(game.conf.magic_conf.power_cfgstats[0].cost[3]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"COST", 4, field(game.conf.magic_conf.power_cfgstats[0].cost[4]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"COST", 5, field(game.conf.magic_conf.power_cfgstats[0].cost[5]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"COST", 6, field(game.conf.magic_conf.power_cfgstats[0].cost[6]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"COST", 7, field(game.conf.magic_conf.power_cfgstats[0].cost[7]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"COST", 7, field(game.conf.magic_conf.power_cfgstats[0].cost[8]), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"DURATION", 0, field(game.conf.magic_conf.power_cfgstats[0].duration), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"CASTABILITY", -1, field(game.conf.magic_conf.power_cfgstats[0].can_cast_flags), 0, 0,UINT64_MAX, (struct NamedCommand*)powermodel_castability_commands, value_longflagsfield, assign_default},
- {"ARTIFACT", 0, field(game.conf.magic_conf.power_cfgstats[0].artifact_model), 0, INT32_MIN,UINT32_MAX, object_desc, value_default, assign_artifact},
- {"NAMETEXTID", 0, field(game.conf.magic_conf.power_cfgstats[0].name_stridx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"TOOLTIPTEXTID", 0, field(game.conf.magic_conf.power_cfgstats[0].tooltip_stridx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"SYMBOLSPRITES", 0, field(game.conf.magic_conf.power_cfgstats[0].bigsym_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
- {"SYMBOLSPRITES", 1, field(game.conf.magic_conf.power_cfgstats[0].medsym_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
- {"POINTERSPRITES", 0, field(game.conf.magic_conf.power_cfgstats[0].pointer_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
- {"PANELTABINDEX", 0, field(game.conf.magic_conf.power_cfgstats[0].panel_tab_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"SOUNDSAMPLES", 0, field(game.conf.magic_conf.power_cfgstats[0].select_sample_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PROPERTIES", 0, field(game.conf.magic_conf.power_cfgstats[0].config_flags), 0, INT32_MIN,UINT32_MAX, powermodel_properties_commands, value_flagsfield,assign_default},
- {"CASTEXPANDFUNC", 0, field(game.conf.magic_conf.power_cfgstats[0].overcharge_check_idx), 0, INT32_MIN,UINT32_MAX, powermodel_expand_check_func_type, value_default, assign_default},
- {"PLAYERSTATE", 0, field(game.conf.magic_conf.power_cfgstats[0].work_state), 0, INT32_MIN,UINT32_MAX, player_state_commands, value_default, assign_default},
- {"PARENTPOWER", 0, field(game.conf.magic_conf.power_cfgstats[0].parent_power), 0, INT32_MIN,UINT32_MAX, power_desc, value_default, assign_default},
- {"SOUNDPLAYED", 0, field(game.conf.magic_conf.power_cfgstats[0].select_sound_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"COOLDOWN", 0, field(game.conf.magic_conf.power_cfgstats[0].cast_cooldown), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"SPELL", 0, field(game.conf.magic_conf.power_cfgstats[0].spell_idx), 0, INT32_MIN,UINT32_MAX, spell_desc, value_default, assign_default},
- {"EFFECT", 0, field(game.conf.magic_conf.power_cfgstats[0].effect_id), 0, INT32_MIN,UINT32_MAX, NULL, value_effOrEffEl,assign_default},
- {"USEFUNCTION", 0, field(game.conf.magic_conf.power_cfgstats[0].magic_use_func_idx), 0, INT32_MIN,UINT32_MAX, magic_use_func_commands, value_function, assign_default},
- {"CREATURETYPE", 0, field(game.conf.magic_conf.power_cfgstats[0].creature_model), 0, INT32_MIN,UINT32_MAX, creature_desc, value_default, assign_default},
- {"COSTFORMULA", 0, field(game.conf.magic_conf.power_cfgstats[0].cost_formula), 0, INT32_MIN,UINT32_MAX, magic_cost_formula_commands, value_default, assign_default},
+ {"NAME", 0, field_t(struct PowerConfigStats, code_name), 0, INT32_MIN,UINT32_MAX, power_desc, value_name, assign_null},
+ {"POWER", 0, field_a(struct PowerConfigStats, strength, 0), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"POWER", 1, field_a(struct PowerConfigStats, strength, 1), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"POWER", 2, field_a(struct PowerConfigStats, strength, 2), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"POWER", 3, field_a(struct PowerConfigStats, strength, 3), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"POWER", 4, field_a(struct PowerConfigStats, strength, 4), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"POWER", 5, field_a(struct PowerConfigStats, strength, 5), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"POWER", 6, field_a(struct PowerConfigStats, strength, 6), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"POWER", 7, field_a(struct PowerConfigStats, strength, 7), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"POWER", 8, field_a(struct PowerConfigStats, strength, 8), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_strength_before_last},
+ {"POWER", 8, field_a(struct PowerConfigStats, strength, 9), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"COST", 0, field_a(struct PowerConfigStats, cost, 0), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"COST", 1, field_a(struct PowerConfigStats, cost, 1), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"COST", 2, field_a(struct PowerConfigStats, cost, 2), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"COST", 3, field_a(struct PowerConfigStats, cost, 3), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"COST", 4, field_a(struct PowerConfigStats, cost, 4), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"COST", 5, field_a(struct PowerConfigStats, cost, 5), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"COST", 6, field_a(struct PowerConfigStats, cost, 6), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"COST", 7, field_a(struct PowerConfigStats, cost, 7), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"COST", 7, field_a(struct PowerConfigStats, cost, 8), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"DURATION", 0, field_t(struct PowerConfigStats, duration), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"CASTABILITY", -1, field_t(struct PowerConfigStats, can_cast_flags), 0, 0,UINT64_MAX, (struct NamedCommand*)powermodel_castability_commands, value_longflagsfield, assign_default},
+ {"ARTIFACT", 0, field_t(struct PowerConfigStats, artifact_model), 0, INT32_MIN,UINT32_MAX, object_desc, value_default, assign_artifact},
+ {"NAMETEXTID", 0, field_t(struct PowerConfigStats, name_stridx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"TOOLTIPTEXTID", 0, field_t(struct PowerConfigStats, tooltip_stridx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"SYMBOLSPRITES", 0, field_t(struct PowerConfigStats, bigsym_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
+ {"SYMBOLSPRITES", 1, field_t(struct PowerConfigStats, medsym_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
+ {"POINTERSPRITES", 0, field_t(struct PowerConfigStats, pointer_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
+ {"PANELTABINDEX", 0, field_t(struct PowerConfigStats, panel_tab_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"SOUNDSAMPLES", 0, field_t(struct PowerConfigStats, select_sample_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PROPERTIES", 0, field_t(struct PowerConfigStats, config_flags), 0, INT32_MIN,UINT32_MAX, powermodel_properties_commands, value_flagsfield,assign_default},
+ {"CASTEXPANDFUNC", 0, field_t(struct PowerConfigStats, overcharge_check_idx), 0, INT32_MIN,UINT32_MAX, powermodel_expand_check_func_type, value_default, assign_default},
+ {"PLAYERSTATE", 0, field_t(struct PowerConfigStats, work_state), 0, INT32_MIN,UINT32_MAX, player_state_commands, value_default, assign_default},
+ {"PARENTPOWER", 0, field_t(struct PowerConfigStats, parent_power), 0, INT32_MIN,UINT32_MAX, power_desc, value_default, assign_default},
+ {"SOUNDPLAYED", 0, field_t(struct PowerConfigStats, select_sound_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"COOLDOWN", 0, field_t(struct PowerConfigStats, cast_cooldown), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"SPELL", 0, field_t(struct PowerConfigStats, spell_idx), 0, INT32_MIN,UINT32_MAX, spell_desc, value_default, assign_default},
+ {"EFFECT", 0, field_t(struct PowerConfigStats, effect_id), 0, INT32_MIN,UINT32_MAX, NULL, value_effOrEffEl,assign_default},
+ {"USEFUNCTION", 0, field_t(struct PowerConfigStats, magic_use_func_idx), 0, INT32_MIN,UINT32_MAX, magic_use_func_commands, value_function, assign_default},
+ {"CREATURETYPE", 0, field_t(struct PowerConfigStats, creature_model), 0, INT32_MIN,UINT32_MAX, creature_desc, value_default, assign_default},
+ {"COSTFORMULA", 0, field_t(struct PowerConfigStats, cost_formula), 0, INT32_MIN,UINT32_MAX, magic_cost_formula_commands, value_default, assign_default},
{NULL},
};
+#pragma pop_macro("game")
+
+static int32_t* get_powers_count(void) { return &game.conf.magic_conf.power_types_count; }
+static void* get_powers_base(void) { return game.conf.magic_conf.power_cfgstats; }
+
const struct NamedFieldSet magic_powers_named_fields_set = {
- &game.conf.magic_conf.power_types_count,
+ get_powers_count,
"power",
magic_powers_named_fields,
power_desc,
MAGIC_ITEMS_MAX,
sizeof(game.conf.magic_conf.power_cfgstats[0]),
- game.conf.magic_conf.power_cfgstats,
+ get_powers_base,
};
#ifdef __cplusplus
diff --git a/src/config_objects.c b/src/config_objects.c
index 1f9ff1cb87..8416e07af5 100644
--- a/src/config_objects.c
+++ b/src/config_objects.c
@@ -16,6 +16,7 @@
* (at your option) any later version.
*/
/******************************************************************************/
+
#include "pre_inc.h"
#include "config_objects.h"
#include "globals.h"
@@ -77,68 +78,75 @@ const struct NamedCommand objects_genres_desc[] = {
{NULL, 0},
};
+#pragma push_macro("game")
+#undef game
static const struct NamedField objects_named_fields[] = {
//name //pos //field //default //min //max //NamedCommand
- {"NAME", 0, field(game.conf.object_conf.object_cfgstats[0].code_name), 0, INT32_MIN,UINT32_MAX, object_desc, value_name, assign_null},
- {"GENRE", 0, field(game.conf.object_conf.object_cfgstats[0].genre), 0, INT32_MIN,UINT32_MAX, objects_genres_desc, value_default, assign_default},
- {"RELATEDCREATURE", 0, field(game.conf.object_conf.object_cfgstats[0].related_creatr_model), 0, INT32_MIN,UINT32_MAX, creature_desc, value_default, assign_default},
- {"PROPERTIES", -1, field(game.conf.object_conf.object_cfgstats[0].model_flags), 0, INT32_MIN,UINT32_MAX, objects_properties_commands, value_flagsfield,assign_default},
- {"ANIMATIONID", 0, field(game.conf.object_conf.object_cfgstats[0].sprite_anim_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_animid, assign_animid},
- {"HANDANIMATIONID", 0, field(game.conf.object_conf.object_cfgstats[0].sprite_anim_idx_in_hand), 0, INT32_MIN,UINT32_MAX, NULL, value_animid, assign_animid},
- {"ANIMATIONSPEED", 0, field(game.conf.object_conf.object_cfgstats[0].anim_speed), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"SIZE_XY", 0, field(game.conf.object_conf.object_cfgstats[0].size_xy), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"SIZE_YZ", 0, field(game.conf.object_conf.object_cfgstats[0].size_z), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"SIZE_Z", 0, field(game.conf.object_conf.object_cfgstats[0].size_z), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"MAXIMUMSIZE", 0, field(game.conf.object_conf.object_cfgstats[0].sprite_size_max), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"DESTROYONLIQUID", 0, field(game.conf.object_conf.object_cfgstats[0].destroy_on_liquid), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"DESTROYONLAVA", 0, field(game.conf.object_conf.object_cfgstats[0].destroy_on_lava), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"HEALTH", 0, field(game.conf.object_conf.object_cfgstats[0].health), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"FALLACCELERATION", 0, field(game.conf.object_conf.object_cfgstats[0].fall_acceleration), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"LIGHTUNAFFECTED", 0, field(game.conf.object_conf.object_cfgstats[0].light_unaffected), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"LIGHTINTENSITY", 0, field(game.conf.object_conf.object_cfgstats[0].ilght.intensity), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"LIGHTRADIUS", 0, field(game.conf.object_conf.object_cfgstats[0].ilght.radius), 0, INT32_MIN,UINT32_MAX, NULL, value_stltocoord,assign_default},
- {"LIGHTISDYNAMIC", 0, field(game.conf.object_conf.object_cfgstats[0].ilght.is_dynamic), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"MAPICON", 0, field(game.conf.object_conf.object_cfgstats[0].map_icon), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
- {"HANDICON", 0, field(game.conf.object_conf.object_cfgstats[0].hand_icon), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
- {"PICKUPOFFSET", 0, field(game.conf.object_conf.object_cfgstats[0].object_picked_up_offset.delta_x), 0,SHRT_MIN,SHRT_MAX, NULL, value_default, assign_default},
- {"PICKUPOFFSET", 1, field(game.conf.object_conf.object_cfgstats[0].object_picked_up_offset.delta_y), 0,SHRT_MIN,SHRT_MAX, NULL, value_default, assign_default},
- {"TOOLTIPTEXTID", 0, field(game.conf.object_conf.object_cfgstats[0].tooltip_stridx), GUIStr_Empty, SHRT_MIN, SHRT_MAX, NULL, value_default, assign_default},
- {"TOOLTIPTEXTID", 1, field(game.conf.object_conf.object_cfgstats[0].tooltip_optional), 0, 0, 1, NULL, value_default, assign_default},
- {"AMBIENCESOUND", 0, field(game.conf.object_conf.object_cfgstats[0].fp_smpl_idx), 0, 0,UINT32_MAX, NULL, value_default, assign_default},
- {"UPDATEFUNCTION", 0, field(game.conf.object_conf.object_cfgstats[0].updatefn_idx), 0, INT32_MIN,UINT32_MAX, object_update_functions_desc,value_function, assign_default},
- {"DRAWCLASS", 0, field(game.conf.object_conf.object_cfgstats[0].draw_class), ODC_Default, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PERSISTENCE", 0, field(game.conf.object_conf.object_cfgstats[0].persistence), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"IMMOBILE", 0, field(game.conf.object_conf.object_cfgstats[0].immobile), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"INITIALSTATE", 0, field(game.conf.object_conf.object_cfgstats[0].initial_state), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"RANDOMSTARTFRAME", 0, field(game.conf.object_conf.object_cfgstats[0].random_start_frame), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"TRANSPARENCYFLAGS", 0, field(game.conf.object_conf.object_cfgstats[0].transparency_flags), 0, INT32_MIN,UINT32_MAX, NULL, value_transpflg, assign_default},
- {"EFFECTBEAM", 0, field(game.conf.object_conf.object_cfgstats[0].effect.beam), 0, INT32_MIN,UINT32_MAX, NULL, value_effOrEffEl,assign_default},
- {"EFFECTPARTICLE", 0, field(game.conf.object_conf.object_cfgstats[0].effect.particle), 0, INT32_MIN,UINT32_MAX, NULL, value_effOrEffEl,assign_default},
- {"EFFECTEXPLOSION1", 0, field(game.conf.object_conf.object_cfgstats[0].effect.explosion1), 0, INT32_MIN,UINT32_MAX, NULL, value_effOrEffEl,assign_default},
- {"EFFECTEXPLOSION2", 0, field(game.conf.object_conf.object_cfgstats[0].effect.explosion2), 0, INT32_MIN,UINT32_MAX, NULL, value_effOrEffEl,assign_default},
- {"EFFECTSPACING", 0, field(game.conf.object_conf.object_cfgstats[0].effect.spacing), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"EFFECTSOUND", 0, field(game.conf.object_conf.object_cfgstats[0].effect.sound_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"EFFECTSOUND", 1, field(game.conf.object_conf.object_cfgstats[0].effect.sound_range), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONID", 0, field(game.conf.object_conf.object_cfgstats[0].flame.animation_id), 0, INT32_MIN,UINT32_MAX, NULL, value_animid, assign_animid},
- {"FLAMEANIMATIONSPEED", 0, field(game.conf.object_conf.object_cfgstats[0].flame.anim_speed), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONSIZE", 0, field(game.conf.object_conf.object_cfgstats[0].flame.sprite_size), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONOFFSET", 0, field(game.conf.object_conf.object_cfgstats[0].flame.fp_add_x), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONOFFSET", 1, field(game.conf.object_conf.object_cfgstats[0].flame.fp_add_y), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONOFFSET", 2, field(game.conf.object_conf.object_cfgstats[0].flame.td_add_x), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONOFFSET", 3, field(game.conf.object_conf.object_cfgstats[0].flame.td_add_y), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMETRANSPARENCYFLAGS", 0, field(game.conf.object_conf.object_cfgstats[0].flame.transparency_flags), 0, INT32_MIN,UINT32_MAX, NULL, value_transpflg, assign_default},
- {"LIGHTFLAGS", 0, field(game.conf.object_conf.object_cfgstats[0].ilght.flags), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"NAME", 0, field_t(struct ObjectConfigStats, code_name), 0, INT32_MIN,UINT32_MAX, object_desc, value_name, assign_null},
+ {"GENRE", 0, field_t(struct ObjectConfigStats, genre), 0, INT32_MIN,UINT32_MAX, objects_genres_desc, value_default, assign_default},
+ {"RELATEDCREATURE", 0, field_t(struct ObjectConfigStats, related_creatr_model), 0, INT32_MIN,UINT32_MAX, creature_desc, value_default, assign_default},
+ {"PROPERTIES", -1, field_t(struct ObjectConfigStats, model_flags), 0, INT32_MIN,UINT32_MAX, objects_properties_commands, value_flagsfield,assign_default},
+ {"ANIMATIONID", 0, field_t(struct ObjectConfigStats, sprite_anim_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_animid, assign_animid},
+ {"HANDANIMATIONID", 0, field_t(struct ObjectConfigStats, sprite_anim_idx_in_hand), 0, INT32_MIN,UINT32_MAX, NULL, value_animid, assign_animid},
+ {"ANIMATIONSPEED", 0, field_t(struct ObjectConfigStats, anim_speed), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"SIZE_XY", 0, field_t(struct ObjectConfigStats, size_xy), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"SIZE_YZ", 0, field_t(struct ObjectConfigStats, size_z), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"SIZE_Z", 0, field_t(struct ObjectConfigStats, size_z), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"MAXIMUMSIZE", 0, field_t(struct ObjectConfigStats, sprite_size_max), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"DESTROYONLIQUID", 0, field_t(struct ObjectConfigStats, destroy_on_liquid), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"DESTROYONLAVA", 0, field_t(struct ObjectConfigStats, destroy_on_lava), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"HEALTH", 0, field_t(struct ObjectConfigStats, health), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"FALLACCELERATION", 0, field_t(struct ObjectConfigStats, fall_acceleration), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"LIGHTUNAFFECTED", 0, field_t(struct ObjectConfigStats, light_unaffected), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"LIGHTINTENSITY", 0, field_t(struct ObjectConfigStats, ilght.intensity), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"LIGHTRADIUS", 0, field_t(struct ObjectConfigStats, ilght.radius), 0, INT32_MIN,UINT32_MAX, NULL, value_stltocoord,assign_default},
+ {"LIGHTISDYNAMIC", 0, field_t(struct ObjectConfigStats, ilght.is_dynamic), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"MAPICON", 0, field_t(struct ObjectConfigStats, map_icon), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
+ {"HANDICON", 0, field_t(struct ObjectConfigStats, hand_icon), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
+ {"PICKUPOFFSET", 0, field_t(struct ObjectConfigStats, object_picked_up_offset.delta_x), 0,SHRT_MIN,SHRT_MAX, NULL, value_default, assign_default},
+ {"PICKUPOFFSET", 1, field_t(struct ObjectConfigStats, object_picked_up_offset.delta_y), 0,SHRT_MIN,SHRT_MAX, NULL, value_default, assign_default},
+ {"TOOLTIPTEXTID", 0, field_t(struct ObjectConfigStats, tooltip_stridx), GUIStr_Empty, SHRT_MIN, SHRT_MAX, NULL, value_default, assign_default},
+ {"TOOLTIPTEXTID", 1, field_t(struct ObjectConfigStats, tooltip_optional), 0, 0, 1, NULL, value_default, assign_default},
+ {"AMBIENCESOUND", 0, field_t(struct ObjectConfigStats, fp_smpl_idx), 0, 0,UINT32_MAX, NULL, value_default, assign_default},
+ {"UPDATEFUNCTION", 0, field_t(struct ObjectConfigStats, updatefn_idx), 0, INT32_MIN,UINT32_MAX, object_update_functions_desc,value_function, assign_default},
+ {"DRAWCLASS", 0, field_t(struct ObjectConfigStats, draw_class), ODC_Default, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PERSISTENCE", 0, field_t(struct ObjectConfigStats, persistence), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"IMMOBILE", 0, field_t(struct ObjectConfigStats, immobile), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"INITIALSTATE", 0, field_t(struct ObjectConfigStats, initial_state), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"RANDOMSTARTFRAME", 0, field_t(struct ObjectConfigStats, random_start_frame), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"TRANSPARENCYFLAGS", 0, field_t(struct ObjectConfigStats, transparency_flags), 0, INT32_MIN,UINT32_MAX, NULL, value_transpflg, assign_default},
+ {"EFFECTBEAM", 0, field_t(struct ObjectConfigStats, effect.beam), 0, INT32_MIN,UINT32_MAX, NULL, value_effOrEffEl,assign_default},
+ {"EFFECTPARTICLE", 0, field_t(struct ObjectConfigStats, effect.particle), 0, INT32_MIN,UINT32_MAX, NULL, value_effOrEffEl,assign_default},
+ {"EFFECTEXPLOSION1", 0, field_t(struct ObjectConfigStats, effect.explosion1), 0, INT32_MIN,UINT32_MAX, NULL, value_effOrEffEl,assign_default},
+ {"EFFECTEXPLOSION2", 0, field_t(struct ObjectConfigStats, effect.explosion2), 0, INT32_MIN,UINT32_MAX, NULL, value_effOrEffEl,assign_default},
+ {"EFFECTSPACING", 0, field_t(struct ObjectConfigStats, effect.spacing), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"EFFECTSOUND", 0, field_t(struct ObjectConfigStats, effect.sound_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"EFFECTSOUND", 1, field_t(struct ObjectConfigStats, effect.sound_range), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONID", 0, field_t(struct ObjectConfigStats, flame.animation_id), 0, INT32_MIN,UINT32_MAX, NULL, value_animid, assign_animid},
+ {"FLAMEANIMATIONSPEED", 0, field_t(struct ObjectConfigStats, flame.anim_speed), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONSIZE", 0, field_t(struct ObjectConfigStats, flame.sprite_size), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONOFFSET", 0, field_t(struct ObjectConfigStats, flame.fp_add_x), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONOFFSET", 1, field_t(struct ObjectConfigStats, flame.fp_add_y), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONOFFSET", 2, field_t(struct ObjectConfigStats, flame.td_add_x), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONOFFSET", 3, field_t(struct ObjectConfigStats, flame.td_add_y), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMETRANSPARENCYFLAGS", 0, field_t(struct ObjectConfigStats, flame.transparency_flags), 0, INT32_MIN,UINT32_MAX, NULL, value_transpflg, assign_default},
+ {"LIGHTFLAGS", 0, field_t(struct ObjectConfigStats, ilght.flags), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
{NULL},
};
+#pragma pop_macro("game")
+
+static int32_t* get_objects_count(void) { return &game.conf.object_conf.object_types_count; }
+static void* get_objects_base(void) { return game.conf.object_conf.object_cfgstats; }
+
const struct NamedFieldSet objects_named_fields_set = {
- &game.conf.object_conf.object_types_count,
+ get_objects_count,
"object",
objects_named_fields,
object_desc,
OBJECT_TYPES_MAX,
sizeof(game.conf.object_conf.object_cfgstats[0]),
- game.conf.object_conf.object_cfgstats,
+ get_objects_base,
};
/******************************************************************************/
diff --git a/src/config_rules.c b/src/config_rules.c
index 157e75e7cb..7594ee14a3 100644
--- a/src/config_rules.c
+++ b/src/config_rules.c
@@ -16,6 +16,7 @@
* (at your option) any later version.
*/
/******************************************************************************/
+
#include "pre_inc.h"
#include "config_rules.h"
#include "globals.h"
@@ -75,139 +76,149 @@ const struct NamedCommand rules_game_classicbugs_commands[] = {
{NULL, 0},
};
+#pragma push_macro("game")
+#undef game
static const struct NamedField rules_game_named_fields[] = {
//name //param //field //default //min //max //namedCommand //valueFunc
- {"POTOFGOLDHOLDS", 0, field(game.conf.rules[0].game.pot_of_gold_holds ), 1000, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
- {"CHESTGOLDHOLD", 0, field(game.conf.rules[0].game.chest_gold_hold ), 1000, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
- {"GOLDPILEVALUE", 0, field(game.conf.rules[0].game.gold_pile_value ), 500, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
- {"GOLDPILEMAXIMUM", 0, field(game.conf.rules[0].game.gold_pile_maximum ), 5000, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
- {"GOLDPERHOARD", 0, field(game.conf.rules[0].game.gold_per_hoard ), 2000, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
- {"FOODLIFEOUTOFHATCHERY", 0, field(game.conf.rules[0].game.food_life_out_of_hatchery ), 100, 0, USHRT_MAX,NULL, value_default, assign_default},
- {"BOULDERREDUCEHEALTHSLAP", 0, field(game.conf.rules[0].game.boulder_reduce_health_slap), 10, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
- {"BOULDERREDUCEHEALTHWALL", 0, field(game.conf.rules[0].game.boulder_reduce_health_wall), 10, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
- {"BOULDERREDUCEHEALTHROOM", 0, field(game.conf.rules[0].game.boulder_reduce_health_room), 10, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
- {"PAYDAYGAP", 0, field(game.conf.rules[0].game.pay_day_gap ), 5000, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
- {"PAYDAYSPEED", 0, field(game.conf.rules[0].game.pay_day_speed ), 100, 0, UINT32_MAX,NULL, value_default, assign_default},
- {"DUNGEONHEARTHEALTIME", 0, field(game.conf.rules[0].game.dungeon_heart_heal_time ), 10, 0, UINT32_MAX,NULL, value_default, assign_default},
- {"DUNGEONHEARTHEALHEALTH", 0, field(game.conf.rules[0].game.dungeon_heart_heal_health ), 1, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
- {"HERODOORWAITTIME", 0, field(game.conf.rules[0].game.hero_door_wait_time ), 100, 0, UINT32_MAX,NULL, value_default, assign_default},
- {"ROOMSELLGOLDBACKPERCENT", 0, field(game.conf.rules[0].game.room_sale_percent ), 50, 0, INT32_MAX,NULL, value_default, assign_default},
- {"DOORSELLVALUEPERCENT", 0, field(game.conf.rules[0].game.door_sale_percent ), 100, 0, INT32_MAX,NULL, value_default, assign_default},
- {"TRAPSELLVALUEPERCENT", 0, field(game.conf.rules[0].game.trap_sale_percent ), 100, 0, INT32_MAX,NULL, value_default, assign_default},
- {"BAGGOLDHOLD", 0, field(game.conf.rules[0].game.bag_gold_hold ), 200, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
- {"ALLIESSHAREVISION", 0, field(game.conf.rules[0].game.allies_share_vision ), 0, 0, 1,NULL, value_default, assign_AlliesShareVision_script},
- {"ALLIESSHAREDROP", 0, field(game.conf.rules[0].game.allies_share_drop ), 0, 0, 1,NULL, value_default, assign_default},
- {"ALLIESSHARECTA", 0, field(game.conf.rules[0].game.allies_share_cta ), 0, 0, 1,NULL, value_default, assign_default},
- {"DISPLAYPORTALLIMIT", 0, field(game.conf.rules[0].game.display_portal_limit ), 0, 0, 1,NULL, value_default, assign_default},
- {"MAXTHINGSINHAND", 0, field(game.conf.rules[0].game.max_things_in_hand ), 8, 0, MAX_THINGS_IN_HAND,NULL, value_default, assign_default},
- {"TORTUREPAYDAY", 0, field(game.conf.rules[0].game.torture_payday ), 50, 0, USHRT_MAX,NULL, value_default, assign_default},
- {"TORTURETRAININGCOST", 0, field(game.conf.rules[0].game.torture_training_cost ), 100, SHRT_MIN, SHRT_MAX,NULL, value_default, assign_default},
- {"TORTURESCAVENGINGCOST", 0, field(game.conf.rules[0].game.torture_scavenging_cost ), 100, SHRT_MIN, SHRT_MAX,NULL, value_default, assign_default},
- {"EASTEREGGSPEECHCHANCE", 0, field(game.conf.rules[0].game.easter_egg_speech_chance ), 2000, 0, INT32_MAX,NULL, value_default, assign_default},
- {"EASTEREGGSPEECHINTERVAL", 0, field(game.conf.rules[0].game.easter_egg_speech_interval), 20000, 0, INT32_MAX,NULL, value_default, assign_default},
- {"GLOBALAMBIENTLIGHT", 0, field(game.conf.rules[0].game.global_ambient_light ), 10, 0, INT32_MAX,NULL, value_default, assign_default},
- {"THINGMINIMUMILLUMINATION", 0, field(game.conf.rules[0].game.thing_minimum_illumination), 32, 0, INT32_MAX,NULL, value_default, assign_default},
- {"LIGHTENABLED", 0, field(game.conf.rules[0].game.light_enabled ), 1, 0, 1,NULL, value_default, assign_default},
- {"MAPCREATURELIMIT", 0, field(game.conf.rules[0].game.creatures_count ), 255, 0, CREATURES_COUNT-2,NULL, value_default, assign_MapCreatureLimit_script},
- {"PRESERVECLASSICBUGS", -1, field(game.conf.rules[0].game.classic_bugs_flags ),ClscBug_None,ClscBug_None, ClscBug_ListEnd,rules_game_classicbugs_commands,value_flagsfield, assign_default},
+ {"POTOFGOLDHOLDS", 0, field_t(struct RulesConfig, game.pot_of_gold_holds), 1000, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
+ {"CHESTGOLDHOLD", 0, field_t(struct RulesConfig, game.chest_gold_hold), 1000, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
+ {"GOLDPILEVALUE", 0, field_t(struct RulesConfig, game.gold_pile_value), 500, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
+ {"GOLDPILEMAXIMUM", 0, field_t(struct RulesConfig, game.gold_pile_maximum), 5000, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
+ {"GOLDPERHOARD", 0, field_t(struct RulesConfig, game.gold_per_hoard), 2000, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
+ {"FOODLIFEOUTOFHATCHERY", 0, field_t(struct RulesConfig, game.food_life_out_of_hatchery), 100, 0, USHRT_MAX,NULL, value_default, assign_default},
+ {"BOULDERREDUCEHEALTHSLAP", 0, field_t(struct RulesConfig, game.boulder_reduce_health_slap), 10, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
+ {"BOULDERREDUCEHEALTHWALL", 0, field_t(struct RulesConfig, game.boulder_reduce_health_wall), 10, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
+ {"BOULDERREDUCEHEALTHROOM", 0, field_t(struct RulesConfig, game.boulder_reduce_health_room), 10, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
+ {"PAYDAYGAP", 0, field_t(struct RulesConfig, game.pay_day_gap), 5000, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
+ {"PAYDAYSPEED", 0, field_t(struct RulesConfig, game.pay_day_speed), 100, 0, UINT32_MAX,NULL, value_default, assign_default},
+ {"DUNGEONHEARTHEALTIME", 0, field_t(struct RulesConfig, game.dungeon_heart_heal_time), 10, 0, UINT32_MAX,NULL, value_default, assign_default},
+ {"DUNGEONHEARTHEALHEALTH", 0, field_t(struct RulesConfig, game.dungeon_heart_heal_health), 1, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
+ {"HERODOORWAITTIME", 0, field_t(struct RulesConfig, game.hero_door_wait_time), 100, 0, UINT32_MAX,NULL, value_default, assign_default},
+ {"ROOMSELLGOLDBACKPERCENT", 0, field_t(struct RulesConfig, game.room_sale_percent), 50, 0, INT32_MAX,NULL, value_default, assign_default},
+ {"DOORSELLVALUEPERCENT", 0, field_t(struct RulesConfig, game.door_sale_percent), 100, 0, INT32_MAX,NULL, value_default, assign_default},
+ {"TRAPSELLVALUEPERCENT", 0, field_t(struct RulesConfig, game.trap_sale_percent), 100, 0, INT32_MAX,NULL, value_default, assign_default},
+ {"BAGGOLDHOLD", 0, field_t(struct RulesConfig, game.bag_gold_hold), 200, INT32_MIN, INT32_MAX,NULL, value_default, assign_default},
+ {"ALLIESSHAREVISION", 0, field_t(struct RulesConfig, game.allies_share_vision), 0, 0, 1,NULL, value_default, assign_AlliesShareVision_script},
+ {"ALLIESSHAREDROP", 0, field_t(struct RulesConfig, game.allies_share_drop), 0, 0, 1,NULL, value_default, assign_default},
+ {"ALLIESSHARECTA", 0, field_t(struct RulesConfig, game.allies_share_cta), 0, 0, 1,NULL, value_default, assign_default},
+ {"DISPLAYPORTALLIMIT", 0, field_t(struct RulesConfig, game.display_portal_limit), 0, 0, 1,NULL, value_default, assign_default},
+ {"MAXTHINGSINHAND", 0, field_t(struct RulesConfig, game.max_things_in_hand), 8, 0, MAX_THINGS_IN_HAND,NULL, value_default, assign_default},
+ {"TORTUREPAYDAY", 0, field_t(struct RulesConfig, game.torture_payday), 50, 0, USHRT_MAX,NULL, value_default, assign_default},
+ {"TORTURETRAININGCOST", 0, field_t(struct RulesConfig, game.torture_training_cost), 100, SHRT_MIN, SHRT_MAX,NULL, value_default, assign_default},
+ {"TORTURESCAVENGINGCOST", 0, field_t(struct RulesConfig, game.torture_scavenging_cost), 100, SHRT_MIN, SHRT_MAX,NULL, value_default, assign_default},
+ {"EASTEREGGSPEECHCHANCE", 0, field_t(struct RulesConfig, game.easter_egg_speech_chance), 2000, 0, INT32_MAX,NULL, value_default, assign_default},
+ {"EASTEREGGSPEECHINTERVAL", 0, field_t(struct RulesConfig, game.easter_egg_speech_interval), 20000, 0, INT32_MAX,NULL, value_default, assign_default},
+ {"GLOBALAMBIENTLIGHT", 0, field_t(struct RulesConfig, game.global_ambient_light), 10, 0, INT32_MAX,NULL, value_default, assign_default},
+ {"THINGMINIMUMILLUMINATION", 0, field_t(struct RulesConfig, game.thing_minimum_illumination), 32, 0, INT32_MAX,NULL, value_default, assign_default},
+ {"LIGHTENABLED", 0, field_t(struct RulesConfig, game.light_enabled), 1, 0, 1,NULL, value_default, assign_default},
+ {"MAPCREATURELIMIT", 0, field_t(struct RulesConfig, game.creatures_count), 255, 0, CREATURES_COUNT-2,NULL, value_default, assign_MapCreatureLimit_script},
+ {"PRESERVECLASSICBUGS", -1, field_t(struct RulesConfig, game.classic_bugs_flags),ClscBug_None,ClscBug_None, ClscBug_ListEnd,rules_game_classicbugs_commands,value_flagsfield, assign_default},
{NULL},
};
static const struct NamedField rules_computer_named_fields[] = {
//name //param //field //default //min //max
- {"DISEASEHPTEMPLEPERCENTAGE", 0, field(game.conf.rules[0].computer.disease_to_temple_pct),500, 0, USHRT_MAX,NULL,value_default,assign_default},
+ {"DISEASEHPTEMPLEPERCENTAGE", 0, field_t(struct RulesConfig, computer.disease_to_temple_pct),500, 0, USHRT_MAX,NULL,value_default,assign_default},
{NULL},
};
static const struct NamedField rules_creatures_named_fields[] = {
//name //param //field //default //min //max //namedCommand //valueFunc
- {"RECOVERYFREQUENCY", 0, field(game.conf.rules[0].creature.recovery_frequency ) , 10, 0, UCHAR_MAX,NULL,value_default, assign_default},
- {"BODYREMAINSFOR", 0, field(game.conf.rules[0].creature.body_remains_for ) ,1000, 0, USHRT_MAX,NULL,value_default, assign_default},
- {"FLEEZONERADIUS", 0, field(game.conf.rules[0].creature.flee_zone_radius ) ,2048, 0, UINT32_MAX,NULL,value_default, assign_default},
- {"GAMETURNSINFLEE", 0, field(game.conf.rules[0].creature.game_turns_in_flee ) , 200, 0, INT32_MAX,NULL,value_default, assign_default},
- {"GAMETURNSUNCONSCIOUS", 0, field(game.conf.rules[0].creature.game_turns_unconscious) ,2000, 0, USHRT_MAX,NULL,value_default, assign_default},
- {"CRITICALHEALTHPERCENTAGE", 0, field(game.conf.rules[0].creature.critical_health_permil) , 125, 0, 100,NULL,value_x10 , assign_default},
- {"STUNEVILENEMYCHANCE", 0, field(game.conf.rules[0].creature.stun_enemy_chance_evil) , 100, 0, 100,NULL,value_default, assign_default},
- {"STUNGOODENEMYCHANCE", 0, field(game.conf.rules[0].creature.stun_enemy_chance_good) , 100, 0, 100,NULL,value_default, assign_default},
- {"STUNWITHOUTPRISONCHANCE", 0, field(game.conf.rules[0].creature.stun_without_prison_chance), 0, 0, 100,NULL,value_default, assign_default},
+ {"RECOVERYFREQUENCY", 0, field_t(struct RulesConfig, creature.recovery_frequency) , 10, 0, UCHAR_MAX,NULL,value_default, assign_default},
+ {"BODYREMAINSFOR", 0, field_t(struct RulesConfig, creature.body_remains_for) ,1000, 0, USHRT_MAX,NULL,value_default, assign_default},
+ {"FLEEZONERADIUS", 0, field_t(struct RulesConfig, creature.flee_zone_radius) ,2048, 0, UINT32_MAX,NULL,value_default, assign_default},
+ {"GAMETURNSINFLEE", 0, field_t(struct RulesConfig, creature.game_turns_in_flee) , 200, 0, INT32_MAX,NULL,value_default, assign_default},
+ {"GAMETURNSUNCONSCIOUS", 0, field_t(struct RulesConfig, creature.game_turns_unconscious) ,2000, 0, USHRT_MAX,NULL,value_default, assign_default},
+ {"CRITICALHEALTHPERCENTAGE", 0, field_t(struct RulesConfig, creature.critical_health_permil) , 125, 0, 100,NULL,value_x10 , assign_default},
+ {"STUNEVILENEMYCHANCE", 0, field_t(struct RulesConfig, creature.stun_enemy_chance_evil) , 100, 0, 100,NULL,value_default, assign_default},
+ {"STUNGOODENEMYCHANCE", 0, field_t(struct RulesConfig, creature.stun_enemy_chance_good) , 100, 0, 100,NULL,value_default, assign_default},
+ {"STUNWITHOUTPRISONCHANCE", 0, field_t(struct RulesConfig, creature.stun_without_prison_chance), 0, 0, 100,NULL,value_default, assign_default},
{NULL},
};
static const struct NamedField rules_magic_named_fields[] = {
//name //param //field //default //min //max //namedCommand //valueFunc
- {"HOLDAUDIENCETIME", 0, field(game.conf.rules[0].magic.hold_audience_time ), 500, 0, INT32_MAX,NULL,value_default, assign_default},
- {"ARMAGEDDONTELEPORTYOURTIMEGAP", 0, field(game.conf.rules[0].magic.armageddon_teleport_your_time_gap ), 10, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"ARMAGEDDONTELEPORTENEMYTIMEGAP", 0, field(game.conf.rules[0].magic.armageddon_teleport_enemy_time_gap), 10, 0, INT32_MAX,NULL,value_default, assign_default},
- {"ARMAGEDDONTELEPORTNEUTRALS", 0, field(game.conf.rules[0].magic.armageddon_teleport_neutrals ), 0, 0, 1,NULL,value_default, assign_default},
- {"ARMAGEDDONCOUNTDOWN", 0, field(game.conf.rules[0].magic.armageddon_count_down ), 500, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"ARMAGEDDONDURATION", 0, field(game.conf.rules[0].magic.armageddon_duration ),4000, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"DISEASETRANSFERPERCENTAGE", 0, field(game.conf.rules[0].magic.disease_transfer_percentage ), 15, 0, CHAR_MAX,NULL,value_default, assign_default},
- {"DISEASELOSEPERCENTAGEHEALTH", 0, field(game.conf.rules[0].magic.disease_lose_percentage_health ), 8, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"DISEASELOSEHEALTHTIME", 0, field(game.conf.rules[0].magic.disease_lose_health_time ), 200, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"MINDISTANCEFORTELEPORT", 0, field(game.conf.rules[0].magic.min_distance_for_teleport ), 20, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"COLLAPSEDUNGEONDAMAGE", 0, field(game.conf.rules[0].magic.collapse_dungeon_damage ), 10, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"TURNSPERCOLLAPSEDUNGEONDAMAGE", 0, field(game.conf.rules[0].magic.turns_per_collapse_dngn_dmg ), 4, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"FRIENDLYFIGHTAREARANGEPERCENT", 0, field(game.conf.rules[0].magic.friendly_fight_area_range_percent ), 0, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"FRIENDLYFIGHTAREADAMAGEPERCENT", 0, field(game.conf.rules[0].magic.friendly_fight_area_damage_percent), 0, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"WEIGHTCALCULATEPUSH", 0, field(game.conf.rules[0].magic.weight_calculate_push ), 0, 0, SHRT_MAX,NULL,value_default, assign_default},
- {"ALLOWINSTANTCHARGEUP", 0, field(game.conf.rules[0].magic.allow_instant_charge_up ), 0, 0, 1,NULL,value_default, assign_default},
+ {"HOLDAUDIENCETIME", 0, field_t(struct RulesConfig, magic.hold_audience_time), 500, 0, INT32_MAX,NULL,value_default, assign_default},
+ {"ARMAGEDDONTELEPORTYOURTIMEGAP", 0, field_t(struct RulesConfig, magic.armageddon_teleport_your_time_gap), 10, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"ARMAGEDDONTELEPORTENEMYTIMEGAP", 0, field_t(struct RulesConfig, magic.armageddon_teleport_enemy_time_gap), 10, 0, INT32_MAX,NULL,value_default, assign_default},
+ {"ARMAGEDDONTELEPORTNEUTRALS", 0, field_t(struct RulesConfig, magic.armageddon_teleport_neutrals), 0, 0, 1,NULL,value_default, assign_default},
+ {"ARMAGEDDONCOUNTDOWN", 0, field_t(struct RulesConfig, magic.armageddon_count_down), 500, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"ARMAGEDDONDURATION", 0, field_t(struct RulesConfig, magic.armageddon_duration),4000, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"DISEASETRANSFERPERCENTAGE", 0, field_t(struct RulesConfig, magic.disease_transfer_percentage), 15, 0, CHAR_MAX,NULL,value_default, assign_default},
+ {"DISEASELOSEPERCENTAGEHEALTH", 0, field_t(struct RulesConfig, magic.disease_lose_percentage_health), 8, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"DISEASELOSEHEALTHTIME", 0, field_t(struct RulesConfig, magic.disease_lose_health_time), 200, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"MINDISTANCEFORTELEPORT", 0, field_t(struct RulesConfig, magic.min_distance_for_teleport), 20, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"COLLAPSEDUNGEONDAMAGE", 0, field_t(struct RulesConfig, magic.collapse_dungeon_damage), 10, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"TURNSPERCOLLAPSEDUNGEONDAMAGE", 0, field_t(struct RulesConfig, magic.turns_per_collapse_dngn_dmg), 4, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"FRIENDLYFIGHTAREARANGEPERCENT", 0, field_t(struct RulesConfig, magic.friendly_fight_area_range_percent), 0, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"FRIENDLYFIGHTAREADAMAGEPERCENT", 0, field_t(struct RulesConfig, magic.friendly_fight_area_damage_percent), 0, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"WEIGHTCALCULATEPUSH", 0, field_t(struct RulesConfig, magic.weight_calculate_push), 0, 0, SHRT_MAX,NULL,value_default, assign_default},
+ {"ALLOWINSTANTCHARGEUP", 0, field_t(struct RulesConfig, magic.allow_instant_charge_up), 0, 0, 1,NULL,value_default, assign_default},
{NULL},
};
static const struct NamedField rules_rooms_named_fields[] = {
//name //param //field //default //min //max //namedCommand //valueFunc
- {"SCAVENGECOSTFREQUENCY", 0, field(game.conf.rules[0].rooms.scavenge_cost_frequency ), 64, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"TEMPLESCAVENGEPROTECTIONTIME", 0, field(game.conf.rules[0].rooms.temple_scavenge_protection_turns),1000, 0, UINT32_MAX,NULL,value_default, assign_default},
- {"TRAINCOSTFREQUENCY", 0, field(game.conf.rules[0].rooms.train_cost_frequency ), 64, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"TORTURECONVERTCHANCE", 0, field(game.conf.rules[0].rooms.torture_convert_chance ), 33, 0, 100,NULL,value_default, assign_default},
- {"TIMESPENTINPRISONWITHOUTBREAK", 0, field(game.conf.rules[0].rooms.time_in_prison_without_break ),2400, 0, UINT32_MAX,NULL,value_default, assign_default},
- {"GHOSTCONVERTCHANCE", 0, field(game.conf.rules[0].rooms.ghost_convert_chance ), 10, 0, 100,NULL,value_default, assign_default},
- {"DEFAULTGENERATESPEED", 0, field(game.conf.rules[0].rooms.default_generate_speed ), 350, 0, USHRT_MAX,NULL,value_default, assign_default},
- {"DEFAULTMAXCREATURESGENERATEENTRANCE", 0, field(game.conf.rules[0].rooms.default_max_crtrs_gen_entrance ), 200, 0, UINT32_MAX,NULL,value_default, assign_default},
- {"FOODGENERATIONSPEED", 0, field(game.conf.rules[0].rooms.food_generation_speed ),2000, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"PRISONSKELETONCHANCE", 0, field(game.conf.rules[0].rooms.prison_skeleton_chance ), 100, 0, 100,NULL,value_default, assign_default},
- {"BODIESFORVAMPIRE", 0, field(game.conf.rules[0].rooms.bodies_for_vampire ), 6, 0, UCHAR_MAX,NULL,value_default, assign_default},
- {"GRAVEYARDCONVERTTIME", 0, field(game.conf.rules[0].rooms.graveyard_convert_time ), 300, 0, USHRT_MAX,NULL,value_default, assign_default},
- {"SCAVENGEGOODALLOWED", 0, field(game.conf.rules[0].rooms.scavenge_good_allowed ), 1, 0, 1,NULL,value_default, assign_default},
- {"SCAVENGENEUTRALALLOWED", 0, field(game.conf.rules[0].rooms.scavenge_neutral_allowed ), 1, 0, 1,NULL,value_default, assign_default},
- {"TIMEBETWEENPRISONBREAK", 0, field(game.conf.rules[0].rooms.time_between_prison_break ), 64, 0, UINT32_MAX,NULL,value_default, assign_default},
- {"PRISONBREAKCHANCE", 0, field(game.conf.rules[0].rooms.prison_break_chance ), 50, 0, UINT32_MAX,NULL,value_default, assign_default},
- {"TORTUREDEATHCHANCE", 0, field(game.conf.rules[0].rooms.torture_death_chance ), 0, 0, 100,NULL,value_default, assign_default},
- {"BARRACKMAXPARTYSIZE", 0, field(game.conf.rules[0].rooms.barrack_max_party_size ), 10, 0, GROUP_MEMBERS_COUNT,NULL,value_default, assign_default},
- {"TRAININGROOMMAXLEVEL", 0, field(game.conf.rules[0].rooms.training_room_max_level ), 0, 0,CREATURE_MAX_LEVEL+1,NULL,value_default, assign_default},
- {"TRAINEFFICIENCY", 0, field(game.conf.rules[0].rooms.train_efficiency ), 256, 0, USHRT_MAX,NULL,value_default, assign_default},
- {"WORKEFFICIENCY", 0, field(game.conf.rules[0].rooms.work_efficiency ), 256, 0, USHRT_MAX,NULL,value_default, assign_default},
- {"RESEARCHEFFICIENCY", 0, field(game.conf.rules[0].rooms.research_efficiency ), 256, 0, USHRT_MAX,NULL,value_default, assign_default},
- {"SCAVENGEEFFICIENCY", 0, field(game.conf.rules[0].rooms.scavenge_efficiency ), 256, 0, USHRT_MAX,NULL,value_default, assign_default},
+ {"SCAVENGECOSTFREQUENCY", 0, field_t(struct RulesConfig, rooms.scavenge_cost_frequency), 64, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"TEMPLESCAVENGEPROTECTIONTIME", 0, field_t(struct RulesConfig, rooms.temple_scavenge_protection_turns),1000, 0, UINT32_MAX,NULL,value_default, assign_default},
+ {"TRAINCOSTFREQUENCY", 0, field_t(struct RulesConfig, rooms.train_cost_frequency), 64, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"TORTURECONVERTCHANCE", 0, field_t(struct RulesConfig, rooms.torture_convert_chance), 33, 0, 100,NULL,value_default, assign_default},
+ {"TIMESPENTINPRISONWITHOUTBREAK", 0, field_t(struct RulesConfig, rooms.time_in_prison_without_break),2400, 0, UINT32_MAX,NULL,value_default, assign_default},
+ {"GHOSTCONVERTCHANCE", 0, field_t(struct RulesConfig, rooms.ghost_convert_chance), 10, 0, 100,NULL,value_default, assign_default},
+ {"DEFAULTGENERATESPEED", 0, field_t(struct RulesConfig, rooms.default_generate_speed), 350, 0, USHRT_MAX,NULL,value_default, assign_default},
+ {"DEFAULTMAXCREATURESGENERATEENTRANCE", 0, field_t(struct RulesConfig, rooms.default_max_crtrs_gen_entrance), 200, 0, UINT32_MAX,NULL,value_default, assign_default},
+ {"FOODGENERATIONSPEED", 0, field_t(struct RulesConfig, rooms.food_generation_speed),2000, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"PRISONSKELETONCHANCE", 0, field_t(struct RulesConfig, rooms.prison_skeleton_chance), 100, 0, 100,NULL,value_default, assign_default},
+ {"BODIESFORVAMPIRE", 0, field_t(struct RulesConfig, rooms.bodies_for_vampire), 6, 0, UCHAR_MAX,NULL,value_default, assign_default},
+ {"GRAVEYARDCONVERTTIME", 0, field_t(struct RulesConfig, rooms.graveyard_convert_time), 300, 0, USHRT_MAX,NULL,value_default, assign_default},
+ {"SCAVENGEGOODALLOWED", 0, field_t(struct RulesConfig, rooms.scavenge_good_allowed), 1, 0, 1,NULL,value_default, assign_default},
+ {"SCAVENGENEUTRALALLOWED", 0, field_t(struct RulesConfig, rooms.scavenge_neutral_allowed), 1, 0, 1,NULL,value_default, assign_default},
+ {"TIMEBETWEENPRISONBREAK", 0, field_t(struct RulesConfig, rooms.time_between_prison_break), 64, 0, UINT32_MAX,NULL,value_default, assign_default},
+ {"PRISONBREAKCHANCE", 0, field_t(struct RulesConfig, rooms.prison_break_chance), 50, 0, UINT32_MAX,NULL,value_default, assign_default},
+ {"TORTUREDEATHCHANCE", 0, field_t(struct RulesConfig, rooms.torture_death_chance), 0, 0, 100,NULL,value_default, assign_default},
+ {"BARRACKMAXPARTYSIZE", 0, field_t(struct RulesConfig, rooms.barrack_max_party_size), 10, 0, GROUP_MEMBERS_COUNT,NULL,value_default, assign_default},
+ {"TRAININGROOMMAXLEVEL", 0, field_t(struct RulesConfig, rooms.training_room_max_level), 0, 0,CREATURE_MAX_LEVEL+1,NULL,value_default, assign_default},
+ {"TRAINEFFICIENCY", 0, field_t(struct RulesConfig, rooms.train_efficiency), 256, 0, USHRT_MAX,NULL,value_default, assign_default},
+ {"WORKEFFICIENCY", 0, field_t(struct RulesConfig, rooms.work_efficiency), 256, 0, USHRT_MAX,NULL,value_default, assign_default},
+ {"RESEARCHEFFICIENCY", 0, field_t(struct RulesConfig, rooms.research_efficiency), 256, 0, USHRT_MAX,NULL,value_default, assign_default},
+ {"SCAVENGEEFFICIENCY", 0, field_t(struct RulesConfig, rooms.scavenge_efficiency), 256, 0, USHRT_MAX,NULL,value_default, assign_default},
{NULL},
};
static const struct NamedField rules_workers_named_fields[] = {
//name //param //field //default //min //max //namedCommand //valueFunc
- {"HITSPERSLAB", 0, field(game.conf.rules[0].workers.hits_per_slab ), 2, 0, UCHAR_MAX,NULL,value_default, assign_default},
- {"DEFAULTIMPDIGDAMAGE", 0, field(game.conf.rules[0].workers.default_imp_dig_damage ), 1, 0, UINT32_MAX,NULL,value_default, assign_default},
- {"DEFAULTIMPDIGOWNDAMAGE", 0, field(game.conf.rules[0].workers.default_imp_dig_own_damage ), 2, 0, UINT32_MAX,NULL,value_default, assign_default},
- {"IMPWORKEXPERIENCE", 0, field(game.conf.rules[0].workers.digger_work_experience ), 0, 0, INT32_MAX,NULL,value_default, assign_default},
- {"DRAGUNCONSCIOUSTOLAIR", 0, field(game.conf.rules[0].workers.drag_to_lair ), 0, 0, 2,NULL,value_default, assign_default},
+ {"HITSPERSLAB", 0, field_t(struct RulesConfig, workers.hits_per_slab), 2, 0, UCHAR_MAX,NULL,value_default, assign_default},
+ {"DEFAULTIMPDIGDAMAGE", 0, field_t(struct RulesConfig, workers.default_imp_dig_damage), 1, 0, UINT32_MAX,NULL,value_default, assign_default},
+ {"DEFAULTIMPDIGOWNDAMAGE", 0, field_t(struct RulesConfig, workers.default_imp_dig_own_damage), 2, 0, UINT32_MAX,NULL,value_default, assign_default},
+ {"IMPWORKEXPERIENCE", 0, field_t(struct RulesConfig, workers.digger_work_experience), 0, 0, INT32_MAX,NULL,value_default, assign_default},
+ {"DRAGUNCONSCIOUSTOLAIR", 0, field_t(struct RulesConfig, workers.drag_to_lair), 0, 0, 2,NULL,value_default, assign_default},
{NULL},
};
static const struct NamedField rules_health_named_fields[] = {
//name //param //field //default //min //max //namedCommand //valueFunc
- {"HUNGERHEALTHLOSS", 0, field(game.conf.rules[0].health.hunger_health_loss ), 1, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"GAMETURNSPERHUNGERHEALTHLOSS", 0, field(game.conf.rules[0].health.turns_per_hunger_health_loss ),100, 0, USHRT_MAX,NULL,value_default, assign_default},
- {"FOODHEALTHGAIN", 0, field(game.conf.rules[0].health.food_health_gain ), 10, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"TORTUREHEALTHLOSS", 0, field(game.conf.rules[0].health.torture_health_loss ), 5, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
- {"GAMETURNSPERTORTUREHEALTHLOSS", 0, field(game.conf.rules[0].health.turns_per_torture_health_loss),100, 0, USHRT_MAX,NULL,value_default, assign_default},
+ {"HUNGERHEALTHLOSS", 0, field_t(struct RulesConfig, health.hunger_health_loss), 1, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"GAMETURNSPERHUNGERHEALTHLOSS", 0, field_t(struct RulesConfig, health.turns_per_hunger_health_loss),100, 0, USHRT_MAX,NULL,value_default, assign_default},
+ {"FOODHEALTHGAIN", 0, field_t(struct RulesConfig, health.food_health_gain), 10, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"TORTUREHEALTHLOSS", 0, field_t(struct RulesConfig, health.torture_health_loss), 5, INT32_MIN, INT32_MAX,NULL,value_default, assign_default},
+ {"GAMETURNSPERTORTUREHEALTHLOSS", 0, field_t(struct RulesConfig, health.turns_per_torture_health_loss),100, 0, USHRT_MAX,NULL,value_default, assign_default},
{NULL},
};
static const struct NamedField rules_script_only_named_fields[] = {
//name //field //min //max
-{"PayDayProgress",0,field(game.pay_day_progress[0]),0,0,INT32_MAX,NULL,value_default,assign_default},
+{"PayDayProgress",0,(void*)(ptrdiff_t)(offsetof(struct Game, pay_day_progress) - offsetof(struct Game, conf.rules)),
+ var_type(((struct Game*)0)->pay_day_progress[0]),0,0,INT32_MAX,NULL,value_default,assign_default},
{NULL},
};
+#pragma pop_macro("game")
+// IntelliSense does not reliably restore object-like macros via pop_macro; suppress the false E0020.
+#ifdef __INTELLISENSE__
+#endif
+
+static void* get_rules_base(void) { return game.conf.rules; }
+
const struct NamedField* ruleblocks[] = {rules_game_named_fields,rules_rooms_named_fields,rules_magic_named_fields,
rules_creatures_named_fields,rules_computer_named_fields,rules_workers_named_fields,rules_health_named_fields,rules_script_only_named_fields};
@@ -220,7 +231,7 @@ const struct NamedFieldSet rules_named_fields_set = {
NULL,
PLAYERS_COUNT,
sizeof(game.conf.rules[0]),
- &game.conf.rules,
+ get_rules_base,
};
@@ -267,7 +278,10 @@ static void assign_MapCreatureLimit_script(const struct NamedField* named_field,
assign_default(named_field,value,named_fields_set,idx,src_str,flags);
if (flag_is_set(flags,ccf_DuringLevel))
{
+#pragma push_macro("game")
+#undef game
short count = setup_excess_creatures_to_leave_or_die(game.conf.rules[idx].game.creatures_count);
+#pragma pop_macro("game")
if (count > 0)
{
SCRPTLOG("Map creature limit reduced, causing %d creatures to leave or die",count);
diff --git a/src/config_terrain.c b/src/config_terrain.c
index 755ac0d9ab..07c5a2ca37 100644
--- a/src/config_terrain.c
+++ b/src/config_terrain.c
@@ -16,6 +16,7 @@
* (at your option) any later version.
*/
/******************************************************************************/
+
#include "pre_inc.h"
#include "config_terrain.h"
#include "globals.h"
@@ -102,73 +103,87 @@ const struct NamedCommand terrain_room_total_capacity_func_type[] = {
{NULL, 0},
};
+#pragma push_macro("game")
+#undef game
static const struct NamedField terrain_slab_named_fields[] = {
//name //field //default //min //max //NamedCommand
- {"NAME", 0, field(game.conf.slab_conf.slab_cfgstats[0].code_name), 0, INT32_MIN,UINT32_MAX, slab_desc, value_name, assign_null},
- {"TOOLTIPTEXTID", 0, field(game.conf.slab_conf.slab_cfgstats[0].tooltip_stridx), GUIStr_Empty, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"BLOCKFLAGSHEIGHT", 0, field(game.conf.slab_conf.slab_cfgstats[0].block_flags_height), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"BLOCKHEALTHINDEX", 0, field(game.conf.slab_conf.slab_cfgstats[0].block_health_index), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"BLOCKFLAGS", -1, field(game.conf.slab_conf.slab_cfgstats[0].block_flags), 0, INT32_MIN,UINT32_MAX, terrain_flags, value_flagsfield, assign_default},
- {"NOBLOCKFLAGS", -1, field(game.conf.slab_conf.slab_cfgstats[0].noblck_flags), 0, INT32_MIN,UINT32_MAX, terrain_flags, value_flagsfield, assign_default},
- {"FILLSTYLE", 0, field(game.conf.slab_conf.slab_cfgstats[0].fill_style), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"CATEGORY", 0, field(game.conf.slab_conf.slab_cfgstats[0].category), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"SLBID", 0, field(game.conf.slab_conf.slab_cfgstats[0].slb_id), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"WIBBLE", 0, field(game.conf.slab_conf.slab_cfgstats[0].wibble), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"ISSAFELAND", 0, field(game.conf.slab_conf.slab_cfgstats[0].is_safe_land), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"ISDIGGABLE", 0, field(game.conf.slab_conf.slab_cfgstats[0].is_diggable), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"WLBTYPE", 0, field(game.conf.slab_conf.slab_cfgstats[0].wlb_type), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"ANIMATED", 0, field(game.conf.slab_conf.slab_cfgstats[0].animated), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"ISOWNABLE", 0, field(game.conf.slab_conf.slab_cfgstats[0].is_ownable), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"INDESTRUCTIBLE", 0, field(game.conf.slab_conf.slab_cfgstats[0].indestructible), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"GOLDHELD", 0, field(game.conf.slab_conf.slab_cfgstats[0].gold_held), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"NAME", 0, field_t(struct SlabConfigStats, code_name), 0, INT32_MIN,UINT32_MAX, slab_desc, value_name, assign_null},
+ {"TOOLTIPTEXTID", 0, field_t(struct SlabConfigStats, tooltip_stridx), GUIStr_Empty, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"BLOCKFLAGSHEIGHT", 0, field_t(struct SlabConfigStats, block_flags_height), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"BLOCKHEALTHINDEX", 0, field_t(struct SlabConfigStats, block_health_index), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"BLOCKFLAGS", -1, field_t(struct SlabConfigStats, block_flags), 0, INT32_MIN,UINT32_MAX, terrain_flags, value_flagsfield, assign_default},
+ {"NOBLOCKFLAGS", -1, field_t(struct SlabConfigStats, noblck_flags), 0, INT32_MIN,UINT32_MAX, terrain_flags, value_flagsfield, assign_default},
+ {"FILLSTYLE", 0, field_t(struct SlabConfigStats, fill_style), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"CATEGORY", 0, field_t(struct SlabConfigStats, category), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"SLBID", 0, field_t(struct SlabConfigStats, slb_id), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"WIBBLE", 0, field_t(struct SlabConfigStats, wibble), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"ISSAFELAND", 0, field_t(struct SlabConfigStats, is_safe_land), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"ISDIGGABLE", 0, field_t(struct SlabConfigStats, is_diggable), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"WLBTYPE", 0, field_t(struct SlabConfigStats, wlb_type), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"ANIMATED", 0, field_t(struct SlabConfigStats, animated), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"ISOWNABLE", 0, field_t(struct SlabConfigStats, is_ownable), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"INDESTRUCTIBLE", 0, field_t(struct SlabConfigStats, indestructible), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"GOLDHELD", 0, field_t(struct SlabConfigStats, gold_held), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
{NULL},
};
+#pragma pop_macro("game")
+
+static int32_t* get_slab_count(void) { return &game.conf.slab_conf.slab_types_count; }
+static void* get_slab_base(void) { return game.conf.slab_conf.slab_cfgstats; }
+
const struct NamedFieldSet terrain_slab_named_fields_set = {
- &game.conf.slab_conf.slab_types_count,
+ get_slab_count,
"slab",
terrain_slab_named_fields,
slab_desc,
TERRAIN_ITEMS_MAX,
sizeof(game.conf.slab_conf.slab_cfgstats[0]),
- game.conf.slab_conf.slab_cfgstats,
+ get_slab_base,
};
+#pragma push_macro("game")
+#undef game
static const struct NamedField terrain_room_named_fields[] = {
//name //pos //field //default //min //max //NamedCommand
- {"NAME", 0, field(game.conf.slab_conf.room_cfgstats[0].code_name), 0, INT32_MIN,UINT32_MAX, room_desc, value_name, assign_null},
- {"COST", 0, field(game.conf.slab_conf.room_cfgstats[0].cost), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"HEALTH", 0, field(game.conf.slab_conf.room_cfgstats[0].health), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"PROPERTIES", -1, field(game.conf.slab_conf.room_cfgstats[0].flags), 0, INT32_MIN,RoCFlg_ListEnd, terrain_room_properties_commands, value_flagsfield,assign_default},
- {"SLABASSIGN", 0, field(game.conf.slab_conf.room_cfgstats[0].assigned_slab), 0, INT32_MIN,UINT32_MAX, slab_desc, value_default, assign_default},
- {"CREATURECREATION", 0, field(game.conf.slab_conf.room_cfgstats[0].creature_creation_model), 0, INT32_MIN,UINT32_MAX, creature_desc, value_default, assign_default},
- {"MESSAGES", 0, field(game.conf.slab_conf.room_cfgstats[0].msg_needed), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"MESSAGES", 1, field(game.conf.slab_conf.room_cfgstats[0].msg_too_small), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"MESSAGES", 2, field(game.conf.slab_conf.room_cfgstats[0].msg_no_route), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"NAMETEXTID", 0, field(game.conf.slab_conf.room_cfgstats[0].name_stridx), GUIStr_Empty, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"TOOLTIPTEXTID", 0, field(game.conf.slab_conf.room_cfgstats[0].tooltip_stridx), GUIStr_Empty, INT32_MIN,UINT32_MAX, NULL, value_default, assign_update_room_tab},
- {"SYMBOLSPRITES", 0, field(game.conf.slab_conf.room_cfgstats[0].bigsym_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
- {"SYMBOLSPRITES", 1, field(game.conf.slab_conf.room_cfgstats[0].medsym_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon_update_room_tab},
- {"POINTERSPRITES", 0, field(game.conf.slab_conf.room_cfgstats[0].pointer_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon_update_room_tab},
- {"PANELTABINDEX", 0, field(game.conf.slab_conf.room_cfgstats[0].panel_tab_idx), 0, 0, 32, NULL, value_default, assign_update_room_tab},
- {"TOTALCAPACITY", 0, field(game.conf.slab_conf.room_cfgstats[0].update_total_capacity_idx), 0, INT32_MIN,UINT32_MAX, terrain_room_total_capacity_func_type,value_default, assign_reinitialise_rooms},
- {"USEDCAPACITY", 0, field(game.conf.slab_conf.room_cfgstats[0].update_storage_in_room_idx), 0, INT32_MIN,UINT32_MAX, terrain_room_used_capacity_func_type, value_default, assign_default},
- {"USEDCAPACITY", 1, field(game.conf.slab_conf.room_cfgstats[0].update_workers_in_room_idx), 0, INT32_MIN,UINT32_MAX, terrain_room_used_capacity_func_type, value_default, assign_reinitialise_rooms},
- {"SLABSYNERGY", 0, field(game.conf.slab_conf.room_cfgstats[0].synergy_slab), 0, INT32_MIN,UINT32_MAX, slab_desc, value_synergy, assign_recalculate_effeciency},
- {"AMBIENTSNDSAMPLE", 0, field(game.conf.slab_conf.room_cfgstats[0].ambient_snd_smp_id), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
- {"ROLES", -1, field(game.conf.slab_conf.room_cfgstats[0].roles), 0, INT32_MIN,UINT32_MAX, room_roles_desc, value_flagsfield,assign_default},
- {"STORAGEHEIGHT", 0, field(game.conf.slab_conf.room_cfgstats[0].storage_height), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"NAME", 0, field_t(struct RoomConfigStats, code_name), 0, INT32_MIN,UINT32_MAX, room_desc, value_name, assign_null},
+ {"COST", 0, field_t(struct RoomConfigStats, cost), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"HEALTH", 0, field_t(struct RoomConfigStats, health), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"PROPERTIES", -1, field_t(struct RoomConfigStats, flags), 0, INT32_MIN,RoCFlg_ListEnd, terrain_room_properties_commands, value_flagsfield,assign_default},
+ {"SLABASSIGN", 0, field_t(struct RoomConfigStats, assigned_slab), 0, INT32_MIN,UINT32_MAX, slab_desc, value_default, assign_default},
+ {"CREATURECREATION", 0, field_t(struct RoomConfigStats, creature_creation_model), 0, INT32_MIN,UINT32_MAX, creature_desc, value_default, assign_default},
+ {"MESSAGES", 0, field_t(struct RoomConfigStats, msg_needed), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"MESSAGES", 1, field_t(struct RoomConfigStats, msg_too_small), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"MESSAGES", 2, field_t(struct RoomConfigStats, msg_no_route), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"NAMETEXTID", 0, field_t(struct RoomConfigStats, name_stridx), GUIStr_Empty, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"TOOLTIPTEXTID", 0, field_t(struct RoomConfigStats, tooltip_stridx), GUIStr_Empty, INT32_MIN,UINT32_MAX, NULL, value_default, assign_update_room_tab},
+ {"SYMBOLSPRITES", 0, field_t(struct RoomConfigStats, bigsym_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon},
+ {"SYMBOLSPRITES", 1, field_t(struct RoomConfigStats, medsym_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon_update_room_tab},
+ {"POINTERSPRITES", 0, field_t(struct RoomConfigStats, pointer_sprite_idx), 0, INT32_MIN,UINT32_MAX, NULL, value_icon, assign_icon_update_room_tab},
+ {"PANELTABINDEX", 0, field_t(struct RoomConfigStats, panel_tab_idx), 0, 0, 32, NULL, value_default, assign_update_room_tab},
+ {"TOTALCAPACITY", 0, field_t(struct RoomConfigStats, update_total_capacity_idx), 0, INT32_MIN,UINT32_MAX, terrain_room_total_capacity_func_type,value_default, assign_reinitialise_rooms},
+ {"USEDCAPACITY", 0, field_t(struct RoomConfigStats, update_storage_in_room_idx), 0, INT32_MIN,UINT32_MAX, terrain_room_used_capacity_func_type, value_default, assign_default},
+ {"USEDCAPACITY", 1, field_t(struct RoomConfigStats, update_workers_in_room_idx), 0, INT32_MIN,UINT32_MAX, terrain_room_used_capacity_func_type, value_default, assign_reinitialise_rooms},
+ {"SLABSYNERGY", 0, field_t(struct RoomConfigStats, synergy_slab), 0, INT32_MIN,UINT32_MAX, slab_desc, value_synergy, assign_recalculate_effeciency},
+ {"AMBIENTSNDSAMPLE", 0, field_t(struct RoomConfigStats, ambient_snd_smp_id), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
+ {"ROLES", -1, field_t(struct RoomConfigStats, roles), 0, INT32_MIN,UINT32_MAX, room_roles_desc, value_flagsfield,assign_default},
+ {"STORAGEHEIGHT", 0, field_t(struct RoomConfigStats, storage_height), 0, INT32_MIN,UINT32_MAX, NULL, value_default, assign_default},
{NULL},
};
+#pragma pop_macro("game")
+
+static int32_t* get_room_count(void) { return &game.conf.slab_conf.room_types_count; }
+static void* get_room_base(void) { return game.conf.slab_conf.room_cfgstats; }
+
const struct NamedFieldSet terrain_room_named_fields_set = {
- &game.conf.slab_conf.room_types_count,
+ get_room_count,
"room",
terrain_room_named_fields,
room_desc,
TERRAIN_ITEMS_MAX,
sizeof(game.conf.slab_conf.room_cfgstats[0]),
- game.conf.slab_conf.room_cfgstats,
+ get_room_base,
};
static void assign_update_room_tab(const struct NamedField* named_field, int64_t value, const struct NamedFieldSet* named_fields_set, int idx, const char* src_str, unsigned char flags)
diff --git a/src/config_trapdoor.c b/src/config_trapdoor.c
index 4ffd621711..c3ad4cd340 100644
--- a/src/config_trapdoor.c
+++ b/src/config_trapdoor.c
@@ -16,6 +16,7 @@
* (at your option) any later version.
*/
/******************************************************************************/
+
#include "pre_inc.h"
#include "config_trapdoor.h"
#include "globals.h"
@@ -251,120 +252,134 @@ static void assign_refresh_trap_anim_anim_id(const struct NamedField* named_fiel
}
}
+#pragma push_macro("game")
+#undef game
const struct NamedField trapdoor_door_named_fields[] = {
//name //pos //field //default //min //max //NamedCommand
- {"NAME", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].code_name), 0, INT32_MIN, UINT32_MAX, door_desc, value_name, assign_null},
- {"NAMETEXTID", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].name_stridx), GUIStr_Empty, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"TOOLTIPTEXTID", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].tooltip_stridx),GUIStr_Empty, INT32_MIN, UINT32_MAX, NULL, value_default, assign_tooltip_idx_door},
- {"SYMBOLSPRITES", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].bigsym_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon},
- {"SYMBOLSPRITES", 1, field(game.conf.trapdoor_conf.door_cfgstats[0].medsym_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon_update_trap_tab},
- {"POINTERSPRITES", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].pointer_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon_update_trap_tab},
- {"PANELTABINDEX", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].panel_tab_idx), 0, 0, 32, NULL, value_default, assign_panel_tab_idx_door},
+ {"NAME", 0, field_t(struct DoorConfigStats, code_name), 0, INT32_MIN, UINT32_MAX, door_desc, value_name, assign_null},
+ {"NAMETEXTID", 0, field_t(struct DoorConfigStats, name_stridx), GUIStr_Empty, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"TOOLTIPTEXTID", 0, field_t(struct DoorConfigStats, tooltip_stridx),GUIStr_Empty, INT32_MIN, UINT32_MAX, NULL, value_default, assign_tooltip_idx_door},
+ {"SYMBOLSPRITES", 0, field_t(struct DoorConfigStats, bigsym_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon},
+ {"SYMBOLSPRITES", 1, field_t(struct DoorConfigStats, medsym_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon_update_trap_tab},
+ {"POINTERSPRITES", 0, field_t(struct DoorConfigStats, pointer_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon_update_trap_tab},
+ {"PANELTABINDEX", 0, field_t(struct DoorConfigStats, panel_tab_idx), 0, 0, 32, NULL, value_default, assign_panel_tab_idx_door},
{"CRATE", 0, NULL,0, 0, INT32_MIN, UINT32_MAX, object_desc, value_default, assign_crate_door},
- {"MANUFACTURELEVEL", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].manufct_level), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"MANUFACTUREREQUIRED", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].manufct_required), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"HEALTH", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].health), 1, INT32_MIN, UINT32_MAX, NULL, value_default, assign_update_door_stats},
- {"SLABKIND", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].slbkind[1]), 0, 0, TERRAIN_ITEMS_MAX, slab_desc, value_default, assign_default},
- {"SLABKIND", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].slbkind[0]), 0, 0, TERRAIN_ITEMS_MAX, slab_desc, value_default, assign_update_door_stats},
- {"OPENSPEED", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].open_speed), 256, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"PROPERTIES", -1, field(game.conf.trapdoor_conf.door_cfgstats[0].model_flags), 0, INT32_MIN, UINT32_MAX, door_properties_commands, value_flagsfield, assign_default},
- {"SELLINGVALUE", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].selling_value), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"UNSELLABLE", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].unsellable), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"PLACESOUND", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].place_sound_idx), 117, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"UPDATEFUNCTION", 0, field(game.conf.trapdoor_conf.door_cfgstats[0].updatefn_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_function, assign_default},
+ {"MANUFACTURELEVEL", 0, field_t(struct DoorConfigStats, manufct_level), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"MANUFACTUREREQUIRED", 0, field_t(struct DoorConfigStats, manufct_required), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"HEALTH", 0, field_t(struct DoorConfigStats, health), 1, INT32_MIN, UINT32_MAX, NULL, value_default, assign_update_door_stats},
+ {"SLABKIND", 0, field_a(struct DoorConfigStats, slbkind, 1), 0, 0, TERRAIN_ITEMS_MAX, slab_desc, value_default, assign_default},
+ {"SLABKIND", 0, field_a(struct DoorConfigStats, slbkind, 0), 0, 0, TERRAIN_ITEMS_MAX, slab_desc, value_default, assign_update_door_stats},
+ {"OPENSPEED", 0, field_t(struct DoorConfigStats, open_speed), 256, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"PROPERTIES", -1, field_t(struct DoorConfigStats, model_flags), 0, INT32_MIN, UINT32_MAX, door_properties_commands, value_flagsfield, assign_default},
+ {"SELLINGVALUE", 0, field_t(struct DoorConfigStats, selling_value), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"UNSELLABLE", 0, field_t(struct DoorConfigStats, unsellable), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"PLACESOUND", 0, field_t(struct DoorConfigStats, place_sound_idx), 117, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"UPDATEFUNCTION", 0, field_t(struct DoorConfigStats, updatefn_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_function, assign_default},
{NULL},
};
+#pragma pop_macro("game")
+
+static int32_t* get_door_count(void) { return &game.conf.trapdoor_conf.door_types_count; }
+static void* get_door_base(void) { return game.conf.trapdoor_conf.door_cfgstats; }
+
const struct NamedFieldSet trapdoor_door_named_fields_set = {
- &game.conf.trapdoor_conf.door_types_count,
+ get_door_count,
"door",
trapdoor_door_named_fields,
door_desc,
TRAPDOOR_TYPES_MAX,
sizeof(game.conf.trapdoor_conf.door_cfgstats[0]),
- game.conf.trapdoor_conf.door_cfgstats,
+ get_door_base,
};
+#pragma push_macro("game")
+#undef game
const struct NamedField trapdoor_trap_named_fields[] = {
//name //pos //field //default //min //max //NamedCommand
- {"NAME", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].code_name), 0, INT32_MIN, UINT32_MAX, door_desc, value_name, assign_null},
- {"MANUFACTURELEVEL", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].manufct_level), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"MANUFACTUREREQUIRED", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].manufct_required), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"SHOTS", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].shots), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"TIMEBETWEENSHOTS", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].shots_delay), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"SELLINGVALUE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].selling_value), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"NAMETEXTID", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].name_stridx), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"TOOLTIPTEXTID", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].tooltip_stridx), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_tooltip_idx_trap},
+ {"NAME", 0, field_t(struct TrapConfigStats, code_name), 0, INT32_MIN, UINT32_MAX, door_desc, value_name, assign_null},
+ {"MANUFACTURELEVEL", 0, field_t(struct TrapConfigStats, manufct_level), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"MANUFACTUREREQUIRED", 0, field_t(struct TrapConfigStats, manufct_required), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"SHOTS", 0, field_t(struct TrapConfigStats, shots), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"TIMEBETWEENSHOTS", 0, field_t(struct TrapConfigStats, shots_delay), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"SELLINGVALUE", 0, field_t(struct TrapConfigStats, selling_value), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"NAMETEXTID", 0, field_t(struct TrapConfigStats, name_stridx), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"TOOLTIPTEXTID", 0, field_t(struct TrapConfigStats, tooltip_stridx), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_tooltip_idx_trap},
{"CRATE", 0, NULL,0, 0, INT32_MIN, UINT32_MAX, object_desc, value_default, assign_crate_trap},
- {"SYMBOLSPRITES", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].bigsym_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon},
- {"SYMBOLSPRITES", 1, field(game.conf.trapdoor_conf.trap_cfgstats[0].medsym_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon_update_trap_tab},
- {"POINTERSPRITES", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].pointer_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon_update_trap_tab},
- {"PANELTABINDEX", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].panel_tab_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_panel_tab_idx_trap},
- {"TRIGGERTYPE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].trigger_type), 0, INT32_MIN, UINT32_MAX, trap_trigger_type_commands,value_default, assign_default},
- {"ACTIVATIONTYPE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].activation_type), 0, INT32_MIN, UINT32_MAX, trap_activation_type_commands,value_default, assign_default},
- {"EFFECTTYPE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].created_itm_model), 0, INT32_MIN, UINT32_MAX, NULL, value_activationeffect, assign_default},
- {"ACTIVATIONLEVEL", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].activation_level), 0, 0, 9, NULL, value_min1, assign_default},
- {"ANIMATIONID", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].sprite_anim_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_animid, assign_refresh_trap_anim_anim_id},
- {"MODEL", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].sprite_anim_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_animid, assign_refresh_trap_anim_anim_id}, // Backward compatibility.
- {"RECHARGEANIMATIONID", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].recharge_sprite_anim_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_animid, assign_refresh_trap_anim_anim_id},
- {"ATTACKANIMATIONID", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].attack_sprite_anim_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_animid, assign_animid},
- {"MODELSIZE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].sprite_size_max), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_refresh_trap_anim},
- {"ANIMATIONSPEED", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].anim_speed), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_multiple_refresh_trap_anim},
- {"ATTACKANIMATIONSPEED", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].attack_anim_speed), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_refresh_trap_anim},
- {"RECHARGEANIMATIONSPEED", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].recharge_anim_speed), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_refresh_trap_anim},
- {"UNANIMATED", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].unanimated), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_refresh_trap_anim},
- {"HIDDEN", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].hidden), true, 0, 1, NULL, value_default, assign_default},
- {"SLAPPABLE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].slappable), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"TRIGGERALARM", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].notify), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"HEALTH", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].health), 1, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"UNSHADED", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].unshaded), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"RANDOMSTARTFRAME", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].random_start_frame), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"THINGSIZE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].size_xy), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"THINGSIZE", 1, field(game.conf.trapdoor_conf.trap_cfgstats[0].size_z), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"HITTYPE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].hit_type), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"LIGHTRADIUS", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].light_radius), 0, INT32_MIN, UINT32_MAX, NULL, value_stltocoord, assign_default},
- {"LIGHTINTENSITY", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].light_intensity), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"LIGHTFLAGS", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].light_flag), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"TRANSPARENCYFLAGS", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].transparency_flag), 0, INT32_MIN, UINT32_MAX, NULL, value_transpflg, assign_default},
- {"SHOTVECTOR", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].shotvector.x), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"SHOTVECTOR", 1, field(game.conf.trapdoor_conf.trap_cfgstats[0].shotvector.y), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"SHOTVECTOR", 2, field(game.conf.trapdoor_conf.trap_cfgstats[0].shotvector.z), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"DESTRUCTIBLE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].destructible), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"UNSTABLE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].unstable), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"UNSELLABLE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].unsellable), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"PLACEONBRIDGE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].place_on_bridge), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"SHOTORIGIN", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].shot_shift_x), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"SHOTORIGIN", 1, field(game.conf.trapdoor_conf.trap_cfgstats[0].shot_shift_y), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"SHOTORIGIN", 2, field(game.conf.trapdoor_conf.trap_cfgstats[0].shot_shift_z), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"PLACESOUND", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].place_sound_idx), 117, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"TRIGGERSOUND", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].trigger_sound_idx), 176, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"DESTROYEDEFFECT", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].destroyed_effect), -TngEffElm_Blast2, INT32_MIN, UINT32_MAX, NULL, value_effOrEffEl, assign_default},
- {"INITIALDELAY", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].initial_delay), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"PLACEONSUBTILE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].place_on_subtile), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONID", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].flame.animation_id), 0, INT32_MIN, UINT32_MAX, NULL, value_animid, assign_refresh_trap_anim_anim_id},
- {"FLAMEANIMATIONSPEED", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].flame.anim_speed), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONSIZE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].flame.sprite_size), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONOFFSET", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].flame.fp_add_x), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONOFFSET", 1, field(game.conf.trapdoor_conf.trap_cfgstats[0].flame.fp_add_y), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONOFFSET", 2, field(game.conf.trapdoor_conf.trap_cfgstats[0].flame.td_add_x), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMEANIMATIONOFFSET", 3, field(game.conf.trapdoor_conf.trap_cfgstats[0].flame.td_add_y), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"FLAMETRANSPARENCYFLAGS", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].flame.transparency_flags), 0, INT32_MIN, UINT32_MAX, NULL, value_transpflg, assign_default},
- {"DETECTINVISIBLE", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].detect_invisible), true, 0, 1, NULL, value_default, assign_default},
- {"INSTANTPLACEMENT", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].instant_placement), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"REMOVEONCEDEPLETED", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].remove_once_depleted), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"FLAGNUMBER", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].flag_number), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
- {"UPDATEFUNCTION", 0, field(game.conf.trapdoor_conf.trap_cfgstats[0].updatefn_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_function,assign_default},
+ {"SYMBOLSPRITES", 0, field_t(struct TrapConfigStats, bigsym_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon},
+ {"SYMBOLSPRITES", 1, field_t(struct TrapConfigStats, medsym_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon_update_trap_tab},
+ {"POINTERSPRITES", 0, field_t(struct TrapConfigStats, pointer_sprite_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_icon, assign_icon_update_trap_tab},
+ {"PANELTABINDEX", 0, field_t(struct TrapConfigStats, panel_tab_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_panel_tab_idx_trap},
+ {"TRIGGERTYPE", 0, field_t(struct TrapConfigStats, trigger_type), 0, INT32_MIN, UINT32_MAX, trap_trigger_type_commands,value_default, assign_default},
+ {"ACTIVATIONTYPE", 0, field_t(struct TrapConfigStats, activation_type), 0, INT32_MIN, UINT32_MAX, trap_activation_type_commands,value_default, assign_default},
+ {"EFFECTTYPE", 0, field_t(struct TrapConfigStats, created_itm_model), 0, INT32_MIN, UINT32_MAX, NULL, value_activationeffect, assign_default},
+ {"ACTIVATIONLEVEL", 0, field_t(struct TrapConfigStats, activation_level), 0, 0, 9, NULL, value_min1, assign_default},
+ {"ANIMATIONID", 0, field_t(struct TrapConfigStats, sprite_anim_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_animid, assign_refresh_trap_anim_anim_id},
+ {"MODEL", 0, field_t(struct TrapConfigStats, sprite_anim_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_animid, assign_refresh_trap_anim_anim_id}, // Backward compatibility.
+ {"RECHARGEANIMATIONID", 0, field_t(struct TrapConfigStats, recharge_sprite_anim_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_animid, assign_refresh_trap_anim_anim_id},
+ {"ATTACKANIMATIONID", 0, field_t(struct TrapConfigStats, attack_sprite_anim_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_animid, assign_animid},
+ {"MODELSIZE", 0, field_t(struct TrapConfigStats, sprite_size_max), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_refresh_trap_anim},
+ {"ANIMATIONSPEED", 0, field_t(struct TrapConfigStats, anim_speed), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_multiple_refresh_trap_anim},
+ {"ATTACKANIMATIONSPEED", 0, field_t(struct TrapConfigStats, attack_anim_speed), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_refresh_trap_anim},
+ {"RECHARGEANIMATIONSPEED", 0, field_t(struct TrapConfigStats, recharge_anim_speed), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_refresh_trap_anim},
+ {"UNANIMATED", 0, field_t(struct TrapConfigStats, unanimated), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_refresh_trap_anim},
+ {"HIDDEN", 0, field_t(struct TrapConfigStats, hidden), true, 0, 1, NULL, value_default, assign_default},
+ {"SLAPPABLE", 0, field_t(struct TrapConfigStats, slappable), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"TRIGGERALARM", 0, field_t(struct TrapConfigStats, notify), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"HEALTH", 0, field_t(struct TrapConfigStats, health), 1, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"UNSHADED", 0, field_t(struct TrapConfigStats, unshaded), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"RANDOMSTARTFRAME", 0, field_t(struct TrapConfigStats, random_start_frame), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"THINGSIZE", 0, field_t(struct TrapConfigStats, size_xy), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"THINGSIZE", 1, field_t(struct TrapConfigStats, size_z), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"HITTYPE", 0, field_t(struct TrapConfigStats, hit_type), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"LIGHTRADIUS", 0, field_t(struct TrapConfigStats, light_radius), 0, INT32_MIN, UINT32_MAX, NULL, value_stltocoord, assign_default},
+ {"LIGHTINTENSITY", 0, field_t(struct TrapConfigStats, light_intensity), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"LIGHTFLAGS", 0, field_t(struct TrapConfigStats, light_flag), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"TRANSPARENCYFLAGS", 0, field_t(struct TrapConfigStats, transparency_flag), 0, INT32_MIN, UINT32_MAX, NULL, value_transpflg, assign_default},
+ {"SHOTVECTOR", 0, field_t(struct TrapConfigStats, shotvector.x), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"SHOTVECTOR", 1, field_t(struct TrapConfigStats, shotvector.y), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"SHOTVECTOR", 2, field_t(struct TrapConfigStats, shotvector.z), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"DESTRUCTIBLE", 0, field_t(struct TrapConfigStats, destructible), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"UNSTABLE", 0, field_t(struct TrapConfigStats, unstable), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"UNSELLABLE", 0, field_t(struct TrapConfigStats, unsellable), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"PLACEONBRIDGE", 0, field_t(struct TrapConfigStats, place_on_bridge), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"SHOTORIGIN", 0, field_t(struct TrapConfigStats, shot_shift_x), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"SHOTORIGIN", 1, field_t(struct TrapConfigStats, shot_shift_y), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"SHOTORIGIN", 2, field_t(struct TrapConfigStats, shot_shift_z), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"PLACESOUND", 0, field_t(struct TrapConfigStats, place_sound_idx), 117, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"TRIGGERSOUND", 0, field_t(struct TrapConfigStats, trigger_sound_idx), 176, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"DESTROYEDEFFECT", 0, field_t(struct TrapConfigStats, destroyed_effect), -TngEffElm_Blast2, INT32_MIN, UINT32_MAX, NULL, value_effOrEffEl, assign_default},
+ {"INITIALDELAY", 0, field_t(struct TrapConfigStats, initial_delay), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"PLACEONSUBTILE", 0, field_t(struct TrapConfigStats, place_on_subtile), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONID", 0, field_t(struct TrapConfigStats, flame.animation_id), 0, INT32_MIN, UINT32_MAX, NULL, value_animid, assign_refresh_trap_anim_anim_id},
+ {"FLAMEANIMATIONSPEED", 0, field_t(struct TrapConfigStats, flame.anim_speed), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONSIZE", 0, field_t(struct TrapConfigStats, flame.sprite_size), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONOFFSET", 0, field_t(struct TrapConfigStats, flame.fp_add_x), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONOFFSET", 1, field_t(struct TrapConfigStats, flame.fp_add_y), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONOFFSET", 2, field_t(struct TrapConfigStats, flame.td_add_x), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMEANIMATIONOFFSET", 3, field_t(struct TrapConfigStats, flame.td_add_y), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAMETRANSPARENCYFLAGS", 0, field_t(struct TrapConfigStats, flame.transparency_flags), 0, INT32_MIN, UINT32_MAX, NULL, value_transpflg, assign_default},
+ {"DETECTINVISIBLE", 0, field_t(struct TrapConfigStats, detect_invisible), true, 0, 1, NULL, value_default, assign_default},
+ {"INSTANTPLACEMENT", 0, field_t(struct TrapConfigStats, instant_placement), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"REMOVEONCEDEPLETED", 0, field_t(struct TrapConfigStats, remove_once_depleted), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"FLAGNUMBER", 0, field_t(struct TrapConfigStats, flag_number), 0, INT32_MIN, UINT32_MAX, NULL, value_default, assign_default},
+ {"UPDATEFUNCTION", 0, field_t(struct TrapConfigStats, updatefn_idx), 0, INT32_MIN, UINT32_MAX, NULL, value_function,assign_default},
{NULL},
};
+#pragma pop_macro("game")
+
+static int32_t* get_trap_count(void) { return &game.conf.trapdoor_conf.trap_types_count; }
+static void* get_trap_base(void) { return game.conf.trapdoor_conf.trap_cfgstats; }
+
const struct NamedFieldSet trapdoor_trap_named_fields_set = {
- &game.conf.trapdoor_conf.trap_types_count,
+ get_trap_count,
"trap",
trapdoor_trap_named_fields,
trap_desc,
TRAPDOOR_TYPES_MAX,
sizeof(game.conf.trapdoor_conf.trap_cfgstats[0]),
- game.conf.trapdoor_conf.trap_cfgstats,
+ get_trap_base,
};
/******************************************************************************/
diff --git a/src/console_cmd.c b/src/console_cmd.c
index bfd1541d24..b0ed9ccdcc 100644
--- a/src/console_cmd.c
+++ b/src/console_cmd.c
@@ -76,7 +76,7 @@
extern "C" {
#endif
-#if defined(__MINGW32__)
+#if defined(__MINGW32__) || defined(_MSC_VER)
// Copied from stack overflow because MingW doesn't provide it.
char *strsep(char ** stringp, const char * delim) {
diff --git a/src/custom_sprites.c b/src/custom_sprites.c
index 529c32d91b..6605d79b4c 100644
--- a/src/custom_sprites.c
+++ b/src/custom_sprites.c
@@ -795,8 +795,10 @@ static int read_png_icon(unzFile zip, const char *path, const char *subpath, int
return 1;
}
+#ifdef __clang__
#pragma clang diagnostic push
#pragma ide diagnostic ignored "bugprone-branch-clone"
+#endif
static int read_png_data(unzFile zip, const char *path, struct SpriteContext *context, const char *subpath,
int fp, VALUE *def, VALUE *itm)
{
@@ -932,7 +934,9 @@ static int read_png_data(unzFile zip, const char *path, struct SpriteContext *co
spng_ctx_free(ctx);
return 1;
}
+#ifdef __clang__
#pragma clang diagnostic pop
+#endif
static void convert_row(unsigned char *dst_buf, uint32_t *src_buf, int len)
{
diff --git a/src/lua_api_things.c b/src/lua_api_things.c
index 3aee220ddc..4fb45f7db5 100644
--- a/src/lua_api_things.c
+++ b/src/lua_api_things.c
@@ -38,7 +38,8 @@
static int thing_set_field(lua_State *L);
static int thing_get_field(lua_State *L);
-static const struct luaL_Reg thing_methods[];
+// Forward reference: defined after thing_get_field. Pointer allows use before definition.
+static const struct luaL_Reg *thing_methods_ptr = NULL;
@@ -406,7 +407,7 @@ static int thing_set_field(lua_State *L) {
static int thing_get_field(lua_State *L) {
const char* key = luaL_checkstring(L, 2);
- if (try_get_c_method(L, key, thing_methods))
+ if (try_get_c_method(L, key, thing_methods_ptr))
{
return 1;
}
@@ -597,7 +598,7 @@ static int thing_eq(lua_State *L) {
}
-static const struct luaL_Reg thing_methods[] = {
+static const struct luaL_Reg thing_methods_arr[] = {
{"make_thing_zombie" ,make_thing_zombie },
{"walk_to" ,lua_creature_walk_to },
{"kill" ,lua_kill_creature },
@@ -624,6 +625,9 @@ static const struct luaL_Reg thing_meta[] = {
};
void Thing_register(lua_State *L) {
+ // Initialise forward-reference pointer now that the array is defined
+ thing_methods_ptr = thing_methods_arr;
+
// Create and register the metatable as "Thing"
luaL_newmetatable(L, "Thing");
@@ -632,7 +636,7 @@ void Thing_register(lua_State *L) {
// Create the method table for Lua-accessible methods
lua_newtable(L);
- luaL_setfuncs(L, thing_methods, 0); // your C methods
+ luaL_setfuncs(L, thing_methods_arr, 0); // your C methods
// Save method table into metatable under __methods
lua_setfield(L, -2, "__methods");
diff --git a/src/main.cpp b/src/main.cpp
index 7632d3c4ef..488c275715 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -146,8 +146,8 @@
short do_draw;
short default_loc_player = 0;
struct StartupParameters start_params;
-char autostart_multiplayer_campaign[80] = "";
-int autostart_multiplayer_level = 0;
+extern "C" char autostart_multiplayer_campaign[80] = "";
+extern "C" int autostart_multiplayer_level = 0;
int32_t game_num_fps;
int32_t game_num_fps_draw_current = 0;
diff --git a/src/net_portforward.cpp b/src/net_portforward.cpp
index 556628a86b..0df91b7104 100644
--- a/src/net_portforward.cpp
+++ b/src/net_portforward.cpp
@@ -16,6 +16,14 @@
* (at your option) any later version.
*/
/******************************************************************************/
+#ifdef _MSC_VER
+// Include winsock2 and windows before project headers to prevent include-order
+// conflicts that leave LPMSG/OLE types undefined (WIN32_LEAN_AND_MEAN race).
+#include
+#include
+#endif
+
+
#include "pre_inc.h"
#include "net_portforward.h"
#include "bflib_basics.h"
diff --git a/src/spritesheet.cpp b/src/spritesheet.cpp
index f6a50d140b..ed1c69001a 100644
--- a/src/spritesheet.cpp
+++ b/src/spritesheet.cpp
@@ -68,12 +68,12 @@ bool load_data_file(TbSpriteSheet & sheet, offset_list & offsets, const char * f
if (LbFileLoadAt(fname, buffer.data()) != data_size) return false;
// populate sprite data
for (size_t i = 0; i < num_sprites; ++i) {
- const auto first = buffer.begin() + offsets[i].first;
- const auto last = buffer.begin() + offsets[i + 1].first;
+ const auto offset = offsets[i].first;
+ const auto size = offsets[i + 1].first - offset;
const auto sprite_idx = offsets[i].second;
auto & sprite = sheet.sprites[sprite_idx];
auto & data = sheet.data[sprite_idx];
- data = std::move(std::vector(first, last));
+ data = std::vector(buffer.data() + offset, buffer.data() + offset + size);
sprite.Data = data.data();
}
return true;
@@ -151,3 +151,11 @@ extern "C" long num_sprites(const TbSpriteSheet * sheet)
}
return sheet->sprites.size();
}
+
+extern "C" void trim_spritesheet(TbSpriteSheet *sheet, long count)
+{
+ if (!sheet || count < 0) return;
+ if ((size_t)count >= sheet->sprites.size()) return;
+ sheet->sprites.resize((size_t)count);
+ sheet->data.resize((size_t)count);
+}
From bf7835727143768eb95980007f7cd977f51f7dfc Mon Sep 17 00:00:00 2001
From: Cerwym <1760289+Cerwym@users.noreply.github.com>
Date: Mon, 23 Mar 2026 11:36:44 +0000
Subject: [PATCH 04/15] feat(build): Add AddressSanitizer support for MSVC and
GCC/Clang
Adds ASan (+ UBSan for GCC/Clang) configuration via KEEPERFX_SANITIZERS option:
- MSVC: /fsanitize=address with debug info and STL annotation workarounds
- GCC/Clang: -fsanitize=address,undefined -fno-omit-frame-pointer
- New CMakePresets for x86/x64 Debug and RelDebug with ASan enabled
- Linux x64 ASan preset for native builds
---
CMakePresets.json | 69 +++++++++++++++++++++++++++++
build/cmake/modules/Platforms.cmake | 20 +++++++++
2 files changed, 89 insertions(+)
diff --git a/CMakePresets.json b/CMakePresets.json
index ab039111c0..eed05b2bb6 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -208,6 +208,50 @@
"CMAKE_EXPORT_COMPILE_COMMANDS": "on"
}
},
+ {
+ "name": "x86-windows-static-debug-asan",
+ "displayName": "MSVC x86 Debug + ASan (static)",
+ "description": "Windows x86 Debug with AddressSanitizer for catching OOB/UAF bugs",
+ "inherits": [ "MSVC-x86-Base", "Debug" ],
+ "cacheVariables": {
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on",
+ "KEEPERFX_SANITIZERS": "ON"
+ }
+ },
+
+ {
+ "name": "x86-windows-static-reldebug-asan",
+ "displayName": "MSVC x86 RelDebug + ASan (static)",
+ "description": "Windows x86 RelWithDebInfo with AddressSanitizer (better perf than Debug)",
+ "inherits": [ "MSVC-x86-Base", "Release" ],
+ "cacheVariables": {
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on",
+ "KEEPERFX_SANITIZERS": "ON"
+ }
+ },
+
+ {
+ "name": "x64-windows-static-debug-asan",
+ "displayName": "MSVC x64 Debug + ASan (static)",
+ "description": "Windows x64 Debug with AddressSanitizer for catching OOB/UAF bugs",
+ "inherits": [ "MSVC-x64-Base", "Debug" ],
+ "cacheVariables": {
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on",
+ "KEEPERFX_SANITIZERS": "ON"
+ }
+ },
+
+ {
+ "name": "x64-windows-static-reldebug-asan",
+ "displayName": "MSVC x64 RelDebug + ASan (static)",
+ "description": "Windows x64 RelWithDebInfo with AddressSanitizer (better perf than Debug)",
+ "inherits": [ "MSVC-x64-Base", "Release" ],
+ "cacheVariables": {
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "on",
+ "KEEPERFX_SANITIZERS": "ON"
+ }
+ },
+
{
"name": "cross-base",
"displayName": "Cross-Compile Base",
@@ -490,6 +534,31 @@
"inherits": "StandardLog",
"configurePreset": "x64-windows-static-release"
},
+ {
+ "name": "x86-windows-static-debug-asan",
+ "displayName": "MSVC x86 Debug + ASan (static)",
+ "inherits": "StandardLog",
+ "configurePreset": "x86-windows-static-debug-asan"
+ },
+ {
+ "name": "x86-windows-static-reldebug-asan",
+ "displayName": "MSVC x86 RelDebug + ASan (static)",
+ "inherits": "StandardLog",
+ "configurePreset": "x86-windows-static-reldebug-asan"
+ },
+ {
+ "name": "x64-windows-static-debug-asan",
+ "displayName": "MSVC x64 Debug + ASan (static)",
+ "inherits": "StandardLog",
+ "configurePreset": "x64-windows-static-debug-asan"
+ },
+ {
+ "name": "x64-windows-static-reldebug-asan",
+ "displayName": "MSVC x64 RelDebug + ASan (static)",
+ "inherits": "StandardLog",
+ "configurePreset": "x64-windows-static-reldebug-asan"
+ },
+
{
"name": "windows-x64-release",
"displayName": "Windows x64 Release",
diff --git a/build/cmake/modules/Platforms.cmake b/build/cmake/modules/Platforms.cmake
index 6f96228a31..3403954a1e 100644
--- a/build/cmake/modules/Platforms.cmake
+++ b/build/cmake/modules/Platforms.cmake
@@ -21,3 +21,23 @@ endif()
add_compile_definitions("KEEPERFX_LUA_AVAILABLE=1")
add_compile_definitions("SDL_MIXER_AVAILABLE=1")
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+# Sanitizers (AddressSanitizer + UndefinedBehaviorSanitizer)
+# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+option(KEEPERFX_SANITIZERS "Enable AddressSanitizer (+ UBSan on GCC/Clang)" OFF)
+if(KEEPERFX_SANITIZERS)
+ if(MSVC)
+ add_compile_options(/fsanitize=address)
+ # MSVC ASan needs the debug info for useful stack traces
+ add_compile_options(/Zi)
+ add_link_options(/DEBUG)
+ # Disable STL container annotations — pre-built vcpkg libs don't have them,
+ # causing LNK2038 mismatch errors (annotate_string / annotate_vector)
+ add_compile_definitions(_DISABLE_STRING_ANNOTATION _DISABLE_VECTOR_ANNOTATION)
+ kfx_status("SANITIZERS" "MSVC AddressSanitizer enabled (/fsanitize=address)")
+ else()
+ add_compile_options(-fsanitize=address,undefined -fno-omit-frame-pointer)
+ add_link_options(-fsanitize=address,undefined)
+ kfx_status("SANITIZERS" "GCC/Clang ASan + UBSan enabled")
+ endif()
+endif()
From e136237335e0a53d38fa93c3c26e7525cb2532a4 Mon Sep 17 00:00:00 2001
From: Cerwym <1760289+Cerwym@users.noreply.github.com>
Date: Mon, 23 Mar 2026 11:36:56 +0000
Subject: [PATCH 05/15] ci: Add MSVC/CMake build validation workflow
Adds a GitHub Actions workflow that validates the MSVC/CMake build path
on every push to master and PR. This catches regressions from developers
who use the MinGW/Make build and may not test CMake compatibility.
- Runs on windows-latest with MSVC x86 toolchain
- Uses existing x86-windows-static-release preset
- Leverages vcpkg binary caching for fast CI turnaround
- Compile-only (no packaging or artifact upload)
---
.github/workflows/build-msvc-cmake.yml | 44 ++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
create mode 100644 .github/workflows/build-msvc-cmake.yml
diff --git a/.github/workflows/build-msvc-cmake.yml b/.github/workflows/build-msvc-cmake.yml
new file mode 100644
index 0000000000..b136645d69
--- /dev/null
+++ b/.github/workflows/build-msvc-cmake.yml
@@ -0,0 +1,44 @@
+name: Build MSVC (CMake)
+
+on:
+ push:
+ branches: [master]
+ pull_request:
+ branches: [master]
+
+jobs:
+ build-msvc-x86:
+ name: "MSVC x86 Release"
+ runs-on: windows-latest
+
+ env:
+ VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Export GitHub Actions cache environment
+ uses: actions/github-script@v7
+ with:
+ script: |
+ core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
+ core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
+
+ - name: Init vcpkg submodule
+ run: git submodule update --init external/vcpkg
+
+ - name: Bootstrap vcpkg
+ shell: cmd
+ run: .\external\vcpkg\bootstrap-vcpkg.bat -disableMetrics
+
+ - uses: ilammy/msvc-dev-cmd@v1
+ with:
+ arch: x86
+
+ - name: Configure
+ run: cmake --preset x86-windows-static-release
+
+ - name: Build
+ run: cmake --build --preset x86-windows-static-release
From d62050af9fc5d488d614fdc17b0deaa6c37c722d Mon Sep 17 00:00:00 2001
From: Cerwym <1760289+Cerwym@users.noreply.github.com>
Date: Mon, 23 Mar 2026 12:07:18 +0000
Subject: [PATCH 06/15] fix: ensure C-linkage for autostart_multiplayer globals
---
src/main.cpp | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/main.cpp b/src/main.cpp
index 488c275715..9ad9d86f65 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -146,8 +146,11 @@
short do_draw;
short default_loc_player = 0;
struct StartupParameters start_params;
-extern "C" char autostart_multiplayer_campaign[80] = "";
-extern "C" int autostart_multiplayer_level = 0;
+// Defined with C linkage so .c translation units can reference them without name mangling
+extern "C" {
+ char autostart_multiplayer_campaign[80] = "";
+ int autostart_multiplayer_level = 0;
+}
int32_t game_num_fps;
int32_t game_num_fps_draw_current = 0;
From 58fa8e05fda2af961ae7c9978c589fbcf684dbf5 Mon Sep 17 00:00:00 2001
From: Cerwym <1760289+Cerwym@users.noreply.github.com>
Date: Mon, 23 Mar 2026 14:51:23 +0000
Subject: [PATCH 07/15] fix: added compile guards for msvc/gcc mkdir macro
---
src/bflib_fileio.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/bflib_fileio.c b/src/bflib_fileio.c
index dc26094a9d..c6dbea3896 100644
--- a/src/bflib_fileio.c
+++ b/src/bflib_fileio.c
@@ -144,9 +144,9 @@ int create_directory_for_file(const char * fname)
while (separator != NULL) {
memcpy(tmp, fname, separator - fname);
tmp[separator - fname] = 0;
-#if defined(_WIN32)
+#if defined(KFX_COMPILER_MSVC)
if (_mkdir(tmp) != 0) {
-#else
+#elif defined(KFX_COMPILER_GCC)
if (mkdir(tmp, 0755) != 0) {
#endif
if (errno != EEXIST) {
From 0c30890e207d6704e889219b8f909360695a35f8 Mon Sep 17 00:00:00 2001
From: Cerwym <1760289+Cerwym@users.noreply.github.com>
Date: Tue, 24 Mar 2026 09:21:43 +0000
Subject: [PATCH 08/15] ci: cache vcpkg binaries to GitHub Packages NuGet feed
Add persistent binary caching via GitHub Packages NuGet alongside
the existing x-gha ephemeral cache. vcpkg's ABI hash keys on
compiler identity, triplet, and port SHA so matching is automatic.
- Add permissions (contents: read, packages: write)
- Register GitHub Packages NuGet source with GITHUB_TOKEN auth
- Push to master/develop: readwrite; PRs: read-only
- Trigger on develop branch alongside master
---
.github/workflows/build-msvc-cmake.yml | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/build-msvc-cmake.yml b/.github/workflows/build-msvc-cmake.yml
index b136645d69..49f0a21be6 100644
--- a/.github/workflows/build-msvc-cmake.yml
+++ b/.github/workflows/build-msvc-cmake.yml
@@ -2,15 +2,19 @@ name: Build MSVC (CMake)
on:
push:
- branches: [master]
+ branches: [master, develop]
pull_request:
- branches: [master]
+ branches: [master, develop]
jobs:
build-msvc-x86:
name: "MSVC x86 Release"
runs-on: windows-latest
+ permissions:
+ contents: read
+ packages: write
+
env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
@@ -26,6 +30,18 @@ jobs:
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
+ - name: Setup vcpkg binary caching via GitHub Packages
+ shell: pwsh
+ env:
+ GH_PACKAGES_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ $owner = "${{ github.repository_owner }}"
+ $source = "https://nuget.pkg.github.com/${owner}/index.json"
+ nuget sources add -Name "GitHub" -Source $source -Username $owner -Password $env:GH_PACKAGES_TOKEN -StorePasswordInClearText
+ nuget setapikey $env:GH_PACKAGES_TOKEN -Source "GitHub"
+ $mode = if ("${{ github.event_name }}" -eq "push") { "readwrite" } else { "read" }
+ echo "VCPKG_BINARY_SOURCES=clear;x-gha,readwrite;nuget,GitHub,${mode}" >> $env:GITHUB_ENV
+
- name: Init vcpkg submodule
run: git submodule update --init external/vcpkg
From b751b5ae2f21f68914bb2f69f1ccbdf35c62fa44 Mon Sep 17 00:00:00 2001
From: Cerwym <1760289+Cerwym@users.noreply.github.com>
Date: Tue, 24 Mar 2026 09:27:22 +0000
Subject: [PATCH 09/15] fix: restore effects.toml gap-filling logic from PR
#4626
Re-add sentinel NULL entries and gap placeholder logic for
load_effects, load_effectsgenerators, and load_effectelements
that was inadvertently removed in the MSVC compatibility commit.
---
src/config_effects.c | 61 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/src/config_effects.c b/src/config_effects.c
index 560561b923..e571e9ca1d 100644
--- a/src/config_effects.c
+++ b/src/config_effects.c
@@ -105,6 +105,7 @@ static void load_effects(VALUE *value, unsigned short flags)
{
char key[64] = "";
VALUE *section;
+ int max_effect_id = -1;
for (int id = 0; id < EFFECTS_TYPES_MAX; id++)
{
{
@@ -116,6 +117,7 @@ static void load_effects(VALUE *value, unsigned short flags)
struct EffectConfigStats *effcst = &game.conf.effects_conf.effect_cfgstats[id];
SET_NAME(section,effect_desc,effcst->code_name);
+ max_effect_id = id;
CONDITIONAL_ASSIGN_ARR2_INT_MINMAX(section,"GenerationAccelXYRange",effcst->accel_xy_min,effcst->accel_xy_max);
CONDITIONAL_ASSIGN_ARR2_INT_MINMAX(section,"GenerationAccelZRange", effcst->accel_z_min, effcst->accel_z_max);
@@ -134,12 +136,32 @@ static void load_effects(VALUE *value, unsigned short flags)
CONDITIONAL_ASSIGN_SPELL(section,"SpellEffect",effcst->spell_effect);
}
}
+
+ // Set sentinel NULL entry to mark the end of valid entries
+ if (max_effect_id >= 0)
+ {
+ if (max_effect_id + 1 < EFFECTS_TYPES_MAX)
+ {
+ effect_desc[max_effect_id + 1].name = NULL;
+ }
+ // Fill any gaps with placeholder entries so get_id() won't terminate early
+ for (int id = 0; id <= max_effect_id; id++)
+ {
+ if (effect_desc[id].name == NULL)
+ {
+ effect_desc[id].num = id;
+ // Use the code_name from the effect config (should be initialized to empty or "NULL")
+ effect_desc[id].name = game.conf.effects_conf.effect_cfgstats[id].code_name;
+ }
+ }
+ }
}
static void load_effectsgenerators(VALUE *value, unsigned short flags)
{
char key[KEY_SIZE];
VALUE *section;
+ int max_effectgen_id = -1;
for (int id = 0; id < EFFECTSGEN_TYPES_MAX; id++)
{
{
@@ -151,6 +173,7 @@ static void load_effectsgenerators(VALUE *value, unsigned short flags)
struct EffectGeneratorConfigStats *effgencst = &game.conf.effects_conf.effectgen_cfgstats[id];
SET_NAME(section,effectgen_desc,effgencst->code_name);
+ max_effectgen_id = id;
CONDITIONAL_ASSIGN_INT(section,"GenerationDelayMin",effgencst->generation_delay_min);
CONDITIONAL_ASSIGN_INT(section,"GenerationDelayMax",effgencst->generation_delay_max);
@@ -165,12 +188,31 @@ static void load_effectsgenerators(VALUE *value, unsigned short flags)
CONDITIONAL_ASSIGN_ARR2_INT(section,"Sound",effgencst->sound_sample_idx,effgencst->sound_sample_rng);
}
}
+ // Set sentinel NULL entry to mark the end of valid entries
+ if (max_effectgen_id >= 0)
+ {
+ if (max_effectgen_id + 1 < EFFECTSGEN_TYPES_MAX)
+ {
+ effectgen_desc[max_effectgen_id + 1].name = NULL;
+ }
+ // Fill any gaps with placeholder entries so get_id() won't terminate early
+ for (int id = 0; id <= max_effectgen_id; id++)
+ {
+ if (effectgen_desc[id].name == NULL)
+ {
+ effectgen_desc[id].num = id;
+ // Use the code_name from the effect generator config (should be initialized to empty or "NULL")
+ effectgen_desc[id].name = game.conf.effects_conf.effectgen_cfgstats[id].code_name;
+ }
+ }
+ }
}
static void load_effectelements(VALUE *value, unsigned short flags)
{
char key[KEY_SIZE];
VALUE *section;
+ int max_effectelement_id = -1;
for (int id = 0; id < EFFECTSELLEMENTS_TYPES_MAX; id++)
{
{
@@ -182,6 +224,7 @@ static void load_effectelements(VALUE *value, unsigned short flags)
struct EffectElementConfigStats *effelcst = &game.conf.effects_conf.effectelement_cfgstats[id];
SET_NAME(section,effectelem_desc,effelcst->code_name);
+ max_effectelement_id = id;
CONDITIONAL_ASSIGN_INT(section,"DrawClass", effelcst->draw_class);
CONDITIONAL_ASSIGN_INT(section,"MoveType", effelcst->move_type);
@@ -228,6 +271,24 @@ static void load_effectelements(VALUE *value, unsigned short flags)
CONDITIONAL_ASSIGN_INT(section,"AffectedByWind", effelcst->affected_by_wind );
}
}
+ // Set sentinel NULL entry to mark the end of valid entries
+ if (max_effectelement_id >= 0)
+ {
+ if (max_effectelement_id + 1 < EFFECTSELLEMENTS_TYPES_MAX)
+ {
+ effectelem_desc[max_effectelement_id + 1].name = NULL;
+ }
+ // Fill any gaps with placeholder entries so get_id() won't terminate early
+ for (int id = 0; id <= max_effectelement_id; id++)
+ {
+ if (effectelem_desc[id].name == NULL)
+ {
+ effectelem_desc[id].num = id;
+ // Use the code_name from the effect element config (should be initialized to empty or "NULL")
+ effectelem_desc[id].name = game.conf.effects_conf.effectelement_cfgstats[id].code_name;
+ }
+ }
+ }
}
static TbBool load_effects_config_file(const char *fname, unsigned short flags)
From 7a02c337d27fc5a0b37adbd8bdbca9b0468b9609 Mon Sep 17 00:00:00 2001
From: Cerwym <1760289+Cerwym@users.noreply.github.com>
Date: Tue, 24 Mar 2026 11:40:41 +0000
Subject: [PATCH 10/15] ci: unify CI build workflow with MinGW, Linux, and MSVC
jobs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Expand build-msvc-cmake.yml into a full CI Build workflow that runs
all three toolchains (MinGW x86, Linux x86_64, MSVC x86) on every
PR push and push to master/develop.
Prototype workflow remains unchanged — triggered only on
ready_for_review/review_requested with artifact uploads.
---
.github/workflows/build-msvc-cmake.yml | 64 +++++++++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/build-msvc-cmake.yml b/.github/workflows/build-msvc-cmake.yml
index 49f0a21be6..54491c032d 100644
--- a/.github/workflows/build-msvc-cmake.yml
+++ b/.github/workflows/build-msvc-cmake.yml
@@ -1,4 +1,4 @@
-name: Build MSVC (CMake)
+name: CI Build
on:
push:
@@ -7,6 +7,68 @@ on:
branches: [master, develop]
jobs:
+ build-mingw-x86:
+ name: "MinGW Windows x86"
+ runs-on: ubuntu-24.04
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - uses: dkfans/setup-cpp@master
+
+ - name: Update system
+ run: |
+ set -eux
+ sudo apt update
+ sudo apt install -y build-essential g++-mingw-w64-i686 libpng16-16t64
+
+ - name: Build
+ run: |
+ set -eux
+ BUILD_NUMBER=$(git rev-list --count HEAD)
+ make BUILD_NUMBER=$BUILD_NUMBER standard
+
+ build-linux-x86_64:
+ name: "Linux x86_64"
+ runs-on: ubuntu-24.04
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Update system
+ run: |
+ set -eux
+ sudo apt update
+ sudo apt install -y \
+ build-essential \
+ pkg-config \
+ curl \
+ libavcodec-dev \
+ libavformat-dev \
+ libavutil-dev \
+ libopenal-dev \
+ libluajit-5.1-dev \
+ libminizip-dev \
+ libnatpmp-dev \
+ libspng-dev \
+ libsdl2-dev \
+ libsdl2-image-dev \
+ libsdl2-mixer-dev \
+ libsdl2-net-dev \
+ libswresample-dev \
+ libminiupnpc-dev \
+ zlib1g-dev
+
+ - name: Build
+ run: |
+ set -eux
+ BUILD_NUMBER=$(git rev-list --count HEAD)
+ make BUILD_NUMBER=$BUILD_NUMBER -f linux.mk
+
build-msvc-x86:
name: "MSVC x86 Release"
runs-on: windows-latest
From 3cdf3b7b729b7cea4d1590d0eaa883dadbc4e880 Mon Sep 17 00:00:00 2001
From: Cerwym <1760289+Cerwym@users.noreply.github.com>
Date: Tue, 24 Mar 2026 11:46:58 +0000
Subject: [PATCH 11/15] fix: use _WIN32 platform guard for mkdir instead of
compiler guard
MinGW is GCC but targets Windows where mkdir takes 1 argument.
The guard must check the platform (_WIN32), not the compiler
(KFX_COMPILER_GCC), to select the correct mkdir signature.
---
src/bflib_fileio.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/bflib_fileio.c b/src/bflib_fileio.c
index c6dbea3896..6947916188 100644
--- a/src/bflib_fileio.c
+++ b/src/bflib_fileio.c
@@ -30,6 +30,9 @@
#include
#include
#include
+#if defined(_WIN32)
+#include
+#endif
#if !defined(_WIN32)
#include
#include
@@ -144,9 +147,9 @@ int create_directory_for_file(const char * fname)
while (separator != NULL) {
memcpy(tmp, fname, separator - fname);
tmp[separator - fname] = 0;
-#if defined(KFX_COMPILER_MSVC)
+#if defined(_WIN32)
if (_mkdir(tmp) != 0) {
-#elif defined(KFX_COMPILER_GCC)
+#else
if (mkdir(tmp, 0755) != 0) {
#endif
if (errno != EEXIST) {
From e32ce085c3e77b96eb33a90950215fad09d951fc Mon Sep 17 00:00:00 2001
From: qqluqq
Date: Tue, 21 Apr 2026 17:05:51 +0200
Subject: [PATCH 12/15] add get_struct_base to field
named_field->field now just an offset instead of a full ptr
---
src/config.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/config.c b/src/config.c
index 6c93f392e6..6fc20e8e60 100644
--- a/src/config.c
+++ b/src/config.c
@@ -469,8 +469,9 @@ int64_t value_default(const struct NamedField* named_field, const char* value_te
int64_t value_name(const struct NamedField* named_field, const char* value_text, const struct NamedFieldSet* named_fields_set, int idx, const char* src_str, unsigned char flags)
{
size_t offset = named_fields_set->struct_size * idx;
- strncpy((char*)named_field->field + offset, value_text, COMMAND_WORD_LEN - 1);
- ((char*)named_field->field + offset)[COMMAND_WORD_LEN - 1] = '\0';
+ char* base = (char*)named_fields_set->get_struct_base() + (ptrdiff_t)named_field->field;
+ strncpy(base + offset, value_text, COMMAND_WORD_LEN - 1);
+ (base + offset)[COMMAND_WORD_LEN - 1] = '\0';
return 0;
}
@@ -666,7 +667,7 @@ int64_t parse_named_field_value(const struct NamedField* named_field, const char
int64_t get_named_field_value(const struct NamedField* named_field, const struct NamedFieldSet* named_fields_set, int idx)
{
- void* field = (char*)named_field->field + named_fields_set->struct_size * idx;
+ void* field = (char*)named_fields_set->get_struct_base() + (ptrdiff_t)named_field->field + named_fields_set->struct_size * idx;
switch (named_field->type)
{
case dt_uchar:
@@ -713,7 +714,7 @@ void assign_null(const struct NamedField* named_field, int64_t value, const stru
void assign_default(const struct NamedField* named_field, int64_t value, const struct NamedFieldSet* named_fields_set, int idx, const char* src_str, unsigned char flags)
{
- void* field = (char*)named_field->field + named_fields_set->struct_size * idx;
+ void* field = (char*)named_fields_set->get_struct_base() + (ptrdiff_t)named_field->field + named_fields_set->struct_size * idx;
switch (named_field->type)
{
case dt_uchar:
@@ -1005,7 +1006,7 @@ void set_defaults(const struct NamedFieldSet* named_fields_set, const char *conf
{
for (int i = 0; i < named_fields_set->max_count; i++)
{
- named_fields_set->names[i].name = (char*)name_NamedField->field + i * named_fields_set->struct_size;
+ named_fields_set->names[i].name = (char*)named_fields_set->get_struct_base() + (ptrdiff_t)name_NamedField->field + i * named_fields_set->struct_size;
named_fields_set->names[i].num = i;
}
named_fields_set->names[named_fields_set->max_count - 1].name = NULL; // must be null for get_id
From 4a99e9c90f9d54376c7dc3fd281e4097ab8da6dc Mon Sep 17 00:00:00 2001
From: qqluqq
Date: Sun, 26 Apr 2026 20:16:23 +0200
Subject: [PATCH 13/15] cmake curl
---
deps/CMakeLists.txt | 7 +++++++
vcpkg.json | 3 ++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index abb25514e9..d3a8d6ceed 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -282,3 +282,10 @@ else()
target_link_libraries(keeperfx PUBLIC natpmp_static)
target_link_libraries(keeperfx_hvlog PUBLIC natpmp_static)
endif()
+
+## curl
+if(MSVC)
+ find_package(CURL CONFIG REQUIRED)
+ target_link_libraries(keeperfx PUBLIC CURL::libcurl)
+ target_link_libraries(keeperfx_hvlog PUBLIC CURL::libcurl)
+endif()
\ No newline at end of file
diff --git a/vcpkg.json b/vcpkg.json
index 1eb1cf7f06..56010e2ad1 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -17,6 +17,7 @@
"zlib",
"minizip",
"luajit",
- "miniupnpc"
+ "miniupnpc",
+ "curl"
]
}
From ed69b9aa49450865d86120a808eef972f74757fd Mon Sep 17 00:00:00 2001
From: PieterVdc
Date: Tue, 28 Apr 2026 17:11:50 +0200
Subject: [PATCH 14/15] fix matchmaking_connect_thread on msvc
---
src/net_matchmaking.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/net_matchmaking.c b/src/net_matchmaking.c
index 270dae4231..589b4da0bf 100644
--- a/src/net_matchmaking.c
+++ b/src/net_matchmaking.c
@@ -313,7 +313,7 @@ static void load_published_public_ips(int udp_ipv4_port, int udp_ipv6_port, Punc
copy_public_ip(1, published_addresses->ipv6, sizeof(published_addresses->ipv6));
}
-static int matchmaking_connect_thread(void *)
+static int matchmaking_connect_thread(void *userdata)
{
if (matchmaking_connect() == 0)
matchmaking_request_list();
From 202a5acd25112da539dcbafaddbc03a0b66c6c36 Mon Sep 17 00:00:00 2001
From: PieterVdc
Date: Wed, 6 May 2026 10:24:52 +0200
Subject: [PATCH 15/15] scrap gfx and sfx submodule
---
.gitmodules | 6 ------
1 file changed, 6 deletions(-)
diff --git a/.gitmodules b/.gitmodules
index 3ae28b34c2..78b7060988 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,9 +1,3 @@
-[submodule "gfx"]
- path = gfx
- url = https://github.com/dkfans/FXGraphics.git
-[submodule "sfx"]
- path = sfx
- url = https://github.com/dkfans/FXSounds.git
[submodule "external/vcpkg"]
path = external/vcpkg
url = https://github.com/microsoft/vcpkg