-
Notifications
You must be signed in to change notification settings - Fork 0
README.md
This page shows how to make a simple OrcKit .vmz mod that replaces one vanilla texture:
res://enemies/orc_top_down_walking.png
The same idea works for other texture files. The important rule is that your mod package must contain the replacement at the exact same res:// path as the game file.
Create a working folder like this:
OrcWalkingTexture/
mod.txt
enemies/
orc_top_down_walking.png
scripts/
texture_mod.gd
pack_vmz.ps1
When packed, the .vmz root must look like this:
mod.txt
enemies/orc_top_down_walking.png
scripts/texture_mod.gd
Do not package an extra parent folder. This is wrong:
OrcWalkingTexture/mod.txt
OrcWalkingTexture/enemies/orc_top_down_walking.png
That would become:
res://OrcWalkingTexture/enemies/orc_top_down_walking.png
and it will not replace:
res://enemies/orc_top_down_walking.png
Put this file at:
OrcWalkingTexture/mod.txt
[mod]
id="orc_walking_texture"
name="Orc Walking Texture"
version="1.0.0"
author="YourName"
priority=0
[autoload]
OrcWalkingTexture="res://scripts/texture_mod.gd"The [autoload] script runs the runtime texture swapper. Keep this section if you want the script-based replacement behavior.
Put your new PNG here:
OrcWalkingTexture/enemies/orc_top_down_walking.png
The filename must be exactly:
orc_top_down_walking.png
The folder must be exactly:
enemies
Together, that becomes the runtime path:
res://enemies/orc_top_down_walking.png
Keep the replacement image compatible with the original. For animated sprite sheets, use the same frame layout, similar dimensions, and the same transparent background style unless you intentionally want to change those.
Put this file at:
OrcWalkingTexture/scripts/texture_mod.gd
extends Node
const VANILLA_TEXTURE_PATH := "res://enemies/orc_top_down_walking.png"
const TARGET_FILE := "orc_top_down_walking.png"
var _replacement_texture: Texture2D = null
func _ready() -> void:
print("[OrcWalkingTexture] loaded")
_replacement_texture = ResourceLoader.load(
VANILLA_TEXTURE_PATH,
"Texture2D",
ResourceLoader.CACHE_MODE_IGNORE
) as Texture2D
if _replacement_texture == null:
push_warning("[OrcWalkingTexture] replacement texture did not load")
return
print("[OrcWalkingTexture] replacement texture mounted")
get_tree().node_added.connect(_on_node_added)
await get_tree().process_frame
_replace_in_tree(get_tree().root)
func _on_node_added(node: Node) -> void:
call_deferred("_replace_in_tree", node)
func _replace_in_tree(node: Node) -> void:
if node == null or _replacement_texture == null:
return
_replace_on_node(node)
for child in node.get_children():
_replace_in_tree(child)
func _replace_on_node(node: Node) -> void:
if node is Sprite2D:
var sprite := node as Sprite2D
if _matches_target(sprite.texture):
sprite.texture = _make_replacement(sprite.texture)
print("[OrcWalkingTexture] patched Sprite2D: " + str(sprite.get_path()))
if node is TextureRect:
var rect := node as TextureRect
if _matches_target(rect.texture):
rect.texture = _make_replacement(rect.texture)
print("[OrcWalkingTexture] patched TextureRect: " + str(rect.get_path()))
if node is AnimatedSprite2D:
_replace_sprite_frames(node as AnimatedSprite2D)
func _replace_sprite_frames(sprite: AnimatedSprite2D) -> void:
var frames := sprite.sprite_frames
if frames == null:
return
for anim in frames.get_animation_names():
var count := frames.get_frame_count(anim)
for i in range(count):
var old_texture := frames.get_frame_texture(anim, i)
if not _matches_target(old_texture):
continue
var duration := frames.get_frame_duration(anim, i)
frames.set_frame(anim, i, _make_replacement(old_texture), duration)
print("[OrcWalkingTexture] patched AnimatedSprite2D frame: " + str(sprite.get_path()))
func _matches_target(texture: Texture2D) -> bool:
if texture == null:
return false
if texture.resource_path == VANILLA_TEXTURE_PATH:
return true
if texture.resource_path.get_file() == TARGET_FILE:
return true
if texture is AtlasTexture:
var atlas := texture as AtlasTexture
if atlas.atlas != null:
if atlas.atlas.resource_path == VANILLA_TEXTURE_PATH:
return true
if atlas.atlas.resource_path.get_file() == TARGET_FILE:
return true
return false
func _make_replacement(old_texture: Texture2D) -> Texture2D:
if old_texture is AtlasTexture:
var old_atlas := old_texture as AtlasTexture
var new_atlas := AtlasTexture.new()
new_atlas.atlas = _replacement_texture
new_atlas.region = old_atlas.region
new_atlas.margin = old_atlas.margin
new_atlas.filter_clip = old_atlas.filter_clip
return new_atlas
return _replacement_textureThe .vmz path replacement is still the main thing that makes the mod work. This script is a runtime safety layer: it scans loaded nodes and replaces any Sprite2D, TextureRect, or AnimatedSprite2D frame that still points at the original texture.
or make a .zip with the mod files and rename the .zip to .vmz
Put this file at:
OrcWalkingTexture/pack_vmz.ps1
$ErrorActionPreference = "Stop"
$modRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$outName = "OrcWalkingTexture.vmz"
$outPath = Join-Path $modRoot $outName
$tmpZip = Join-Path $modRoot "OrcWalkingTexture.zip"
if (Test-Path -LiteralPath $outPath) {
Remove-Item -LiteralPath $outPath -Force
}
if (Test-Path -LiteralPath $tmpZip) {
Remove-Item -LiteralPath $tmpZip -Force
}
$files = @(
"mod.txt",
"enemies/orc_top_down_walking.png",
"scripts/texture_mod.gd"
)
foreach ($file in $files) {
$full = Join-Path $modRoot $file
if (-not (Test-Path -LiteralPath $full)) {
throw "Missing required file: $file"
}
}
Compress-Archive `
-Path `
(Join-Path $modRoot "mod.txt"), `
(Join-Path $modRoot "enemies"), `
(Join-Path $modRoot "scripts") `
-DestinationPath $tmpZip `
-Force
Rename-Item -LiteralPath $tmpZip -NewName $outName
Write-Host "Created $outPath"Run it in PowerShell:
cd "C:\Path\To\OrcWalkingTexture"
.\pack_vmz.ps1You should get:
OrcWalkingTexture.vmz
.vmz is zip-compatible. OrcKit treats it as a mod archive and mounts it into the game.
Copy the .vmz into the game mods folder:
C:\Program Files (x86)\Steam\steamapps\common\Sir, We Have an Orc Problem Playtest\mods\OrcWalkingTexture.vmz
Launch the game, open the OrcKit Mods menu, and make sure the mod is enabled.
If another mod also replaces the same texture, put this mod later/higher in load order so it wins.
OrcWalkingTexture/
mod.txt
pack_vmz.ps1
enemies/
orc_top_down_walking.png
scripts/
texture_mod.gd
After launching the game, check the Godot log for:
[OrcWalkingTexture] loaded
[OrcWalkingTexture] replacement texture mounted
If you see replacement texture did not load, the path inside the .vmz is wrong or the PNG is missing from the archive.
Wrong: extra folder inside the .vmz.
OrcWalkingTexture/enemies/orc_top_down_walking.png
Correct inside the .vmz:
enemies/orc_top_down_walking.png
Wrong: filename is close but not exact.
enemies/orc-top-down-walking.png
enemies/orc_top_down_walk.png
Correct:
enemies/orc_top_down_walking.png
Wrong: mod.txt is nested.
OrcWalkingTexture/mod.txt
Correct inside the .vmz:
mod.txt
- Mod is packed as
.vmz. -
mod.txtis at the archive root. - File is named exactly
orc_top_down_walking.png. - File is inside
enemies/at the archive root. - Runtime path is exactly
res://enemies/orc_top_down_walking.png. - Runtime replacement script is at
res://scripts/texture_mod.gd. -
.vmzis placed in the gamemodsfolder. - Mod is enabled in OrcKit.
- Load order is higher/later than other mods replacing the same texture.
OrcKit developer wiki for Sir, We Have an Orc Problem Playtest mods. These pages document OrcKit 1.0.0.