Adding modding folder with a hacked build.

This commit is contained in:
ml
2026-09-26 08:53:08 +02:00
parent cdb960dc71
commit c91080aba7
755 changed files with 50424 additions and 0 deletions
+409
View File
@@ -0,0 +1,409 @@
package levels
import (
"fmt"
"github.com/inkyblackness/imgui-go/v3"
"github.com/inkyblackness/hacked/editor/graphics"
"github.com/inkyblackness/hacked/editor/render"
"github.com/inkyblackness/hacked/ss1/content/archive"
"github.com/inkyblackness/hacked/ss1/content/archive/level"
"github.com/inkyblackness/hacked/ss1/content/text"
"github.com/inkyblackness/hacked/ss1/edit"
"github.com/inkyblackness/hacked/ss1/edit/undoable/cmd"
"github.com/inkyblackness/hacked/ss1/resource"
"github.com/inkyblackness/hacked/ss1/world"
"github.com/inkyblackness/hacked/ss1/world/ids"
"github.com/inkyblackness/hacked/ui/gui"
)
// ControlView is the core view for level editing.
type ControlView struct {
levels *edit.EditableLevels
levelSelection *edit.LevelSelectionService
editor *edit.LevelEditorService
guiScale float32
registry cmd.Registry
textCache *text.Cache
textureCache *graphics.TextureCache
model controlViewModel
}
// NewControlView returns a new instance.
func NewControlView(levels *edit.EditableLevels, levelSelection *edit.LevelSelectionService, editor *edit.LevelEditorService,
guiScale float32, textCache *text.Cache, textureCache *graphics.TextureCache,
registry cmd.Registry) *ControlView {
view := &ControlView{
levels: levels,
levelSelection: levelSelection,
editor: editor,
guiScale: guiScale,
registry: registry,
textCache: textCache,
textureCache: textureCache,
model: freshControlViewModel(),
}
return view
}
// WindowOpen returns the flag address, to be used with the main menu.
func (view *ControlView) WindowOpen() *bool {
return &view.model.windowOpen
}
// Render renders the view.
func (view *ControlView) Render() {
if view.model.restoreFocus {
imgui.SetNextWindowFocus()
view.model.restoreFocus = false
view.model.windowOpen = true
}
if view.model.windowOpen {
imgui.SetNextWindowSizeV(imgui.Vec2{X: 400 * view.guiScale, Y: 300 * view.guiScale}, imgui.ConditionFirstUseEver)
title := "Level Control"
readOnly := view.editor.IsReadOnly()
if readOnly {
title += hintReadOnly
}
if imgui.BeginV(title+"###Level Control", view.WindowOpen(), imgui.WindowFlagsNoCollapse) {
view.renderContent(view.editor.Level(), readOnly)
}
imgui.End()
}
}
var levelHeights = []string{
"32 Tiles",
"16 Tiles",
"8 Tiles",
"4 Tiles",
"2 Tiles",
"1 Tile",
"1/2 Tile",
"1/4 Tile",
}
func (view *ControlView) renderContent(lvl *level.Level, readOnly bool) {
imgui.PushItemWidth(-200 * view.guiScale)
selectedLevel := view.levelSelection.CurrentLevelID()
if gui.StepSliderInt("Active Level", &selectedLevel, 0, archive.MaxLevels-1) {
view.levelSelection.SetCurrentLevelID(selectedLevel)
}
imgui.Separator()
levelType := "Real World"
if lvl.IsCyberspace() {
levelType = "Cyberspace"
}
imgui.LabelText("Type", levelType)
view.renderLevelHeight(lvl, readOnly)
if !lvl.IsCyberspace() {
view.renderTextureAtlas(lvl, readOnly)
view.renderSurveillanceObjects(lvl, readOnly)
view.renderHazards(lvl, readOnly)
view.renderTextureAnimations(lvl, readOnly)
}
imgui.PopItemWidth()
}
func (view *ControlView) renderLevelHeight(lvl *level.Level, readOnly bool) {
_, _, currentShift := lvl.Size()
if readOnly {
imgui.LabelText("Level Height", levelHeights[currentShift])
} else if imgui.BeginCombo("Level Height", levelHeights[currentShift]) {
for shift, height := range levelHeights {
if imgui.SelectableV(height, shift == int(currentShift), 0, imgui.Vec2{}) {
view.requestSetZShift(lvl, shift)
}
}
imgui.EndCombo()
}
}
func (view *ControlView) textureName(index int) string {
key := resource.KeyOf(ids.TextureNames, resource.LangDefault, index)
name, err := view.textCache.Text(key)
suffix := ""
if err == nil {
suffix = ": " + name
}
return fmt.Sprintf("%3d", index) + suffix
}
func (view *ControlView) renderTextureAtlas(lvl *level.Level, readOnly bool) {
imgui.Separator()
atlas := lvl.TextureAtlas()
{
render.TextureSelector("Level Textures", -200, view.guiScale,
len(atlas), view.model.selectedAtlasIndex,
view.textureCache,
func(index int) resource.Key {
return resource.KeyOf(ids.LargeTextures.Plus(int(atlas[index])), resource.LangAny, 0)
},
func(index int) string {
textureType := "(F/C/W)"
if index >= level.FloorCeilingTextureLimit {
textureType = "(walls only)"
}
return fmt.Sprintf("Atlas Index %2d ", index) + textureType + "\n" + view.textureName(int(atlas[index]))
}, func(newIndex int) {
view.model.selectedAtlasIndex = newIndex
})
imgui.SameLine()
imgui.Text("Level Textures")
}
gameTextureIndex := -1
if (view.model.selectedAtlasIndex >= 0) && (view.model.selectedAtlasIndex < len(atlas)) {
gameTextureIndex = int(atlas[view.model.selectedAtlasIndex])
}
if !readOnly {
render.TextureSelector("Game Textures", -200, view.guiScale,
world.MaxWorldTextures, gameTextureIndex,
view.textureCache,
func(index int) resource.Key {
return resource.KeyOf(ids.LargeTextures.Plus(index), resource.LangAny, 0)
},
view.textureName,
func(newIndex int) {
view.requestSetLevelTexture(lvl, view.model.selectedAtlasIndex, newIndex)
})
imgui.SameLine()
imgui.Text("Game Textures")
}
}
func (view *ControlView) renderSurveillanceObjects(lvl *level.Level, readOnly bool) {
imgui.Separator()
if imgui.BeginCombo("Surveillance Object", fmt.Sprintf("Object %d", view.model.selectedSurveillanceObjectIndex)) {
for i := 0; i < level.SurveillanceObjectCount; i++ {
if imgui.SelectableV(fmt.Sprintf("Object %d", i), i == view.model.selectedSurveillanceObjectIndex, 0, imgui.Vec2{}) {
view.model.selectedSurveillanceObjectIndex = i
}
}
imgui.EndCombo()
}
sources := lvl.SurveillanceSources()
surrogates := lvl.SurveillanceSurrogates()
capacity := lvl.ObjectCapacity()
view.renderSliderInt(readOnly, "Surveillance Source", int(sources[view.model.selectedSurveillanceObjectIndex]),
func(int) string { return "%d" },
0, capacity,
func(newValue int) {
view.requestSetSurveillanceSource(lvl, view.model.selectedSurveillanceObjectIndex, level.ObjectID(newValue))
})
view.renderSliderInt(readOnly, "Surveillance Surrogate", int(surrogates[view.model.selectedSurveillanceObjectIndex]),
func(int) string { return "%d" },
0, capacity,
func(newValue int) {
view.requestSetSurveillanceSurrogate(lvl, view.model.selectedSurveillanceObjectIndex, level.ObjectID(newValue))
})
}
func (view *ControlView) renderHazards(lvl *level.Level, readOnly bool) {
imgui.Separator()
parameters := lvl.Parameters()
currentCeiling := currentCeilingHazard(parameters)
if readOnly {
imgui.LabelText("Ceiling Hazard", currentCeiling.title)
} else if imgui.BeginCombo("Ceiling Hazard", currentCeiling.title) {
for _, info := range ceilingHazards {
if imgui.SelectableV(info.title, info.title == currentCeiling.title, 0, imgui.Vec2{}) {
view.requestSetCeilingHazard(lvl, info)
}
}
imgui.EndCombo()
}
view.renderSliderInt(readOnly, "Ceiling Hazard Level", int(parameters.CeilingHazardLevel),
currentCeiling.formatter, 0, 255,
func(newValue int) {
view.requestSetCeilingHazardLevel(lvl, byte(newValue))
})
currentFloor := currentFloorHazard(parameters)
if readOnly {
imgui.LabelText("Floor Hazard", currentFloor.title)
} else if imgui.BeginCombo("Floor Hazard", currentFloor.title) {
for _, info := range floorHazards {
if imgui.SelectableV(info.title, info.title == currentFloor.title, 0, imgui.Vec2{}) {
view.requestSetFloorHazard(lvl, info)
}
}
imgui.EndCombo()
}
view.renderSliderInt(readOnly, "Floor Hazard Level", int(parameters.FloorHazardLevel),
currentFloor.formatter, 0, 255,
func(newValue int) {
view.requestSetFloorHazardLevel(lvl, byte(newValue))
})
}
func (view *ControlView) renderTextureAnimations(lvl *level.Level, readOnly bool) {
imgui.Separator()
animations := lvl.TextureAnimations()
selectedText := ""
if (view.model.selectedTextureAnimationIndex >= 1) && (view.model.selectedTextureAnimationIndex < len(animations)) {
selectedText = fmt.Sprintf("%d", view.model.selectedTextureAnimationIndex)
}
if imgui.BeginCombo("Texture Animation Group", selectedText) {
for i := 1; i < len(animations); i++ {
if imgui.SelectableV(fmt.Sprintf("%2d", i), view.model.selectedTextureAnimationIndex == i, 0, imgui.Vec2{}) {
view.model.selectedTextureAnimationIndex = i
}
}
imgui.EndCombo()
}
if (view.model.selectedTextureAnimationIndex >= 1) && (view.model.selectedTextureAnimationIndex < len(animations)) {
animation := animations[view.model.selectedTextureAnimationIndex]
view.renderSliderInt(readOnly, "Texture Animation Time", int(animation.FrameTime),
func(int) string { return "%d msec" },
0, 1000,
func(newValue int) {
view.requestSetTextureAnimationTime(lvl, view.model.selectedTextureAnimationIndex, uint16(newValue))
})
view.renderSliderInt(readOnly, "Texture Animation Frame Count", int(animation.FrameCount),
func(int) string { return "%d" },
0, 10,
func(newValue int) {
view.requestSetTextureAnimationFrameCount(lvl, view.model.selectedTextureAnimationIndex, byte(newValue))
})
if readOnly {
imgui.LabelText("Texture Animation Loop Type", animation.LoopType.String())
} else if imgui.BeginCombo("Texture Animation Loop Type", animation.LoopType.String()) {
loopTypes := level.TextureAnimationLoopTypes()
for _, loopType := range loopTypes {
if imgui.SelectableV(loopType.String(), loopType == animation.LoopType, 0, imgui.Vec2{}) {
view.requestSetTextureAnimationType(lvl, view.model.selectedTextureAnimationIndex, loopType)
}
}
imgui.EndCombo()
}
}
}
func (view *ControlView) renderSliderInt(readOnly bool, label string, selectedValue int,
formatter func(int) string, min, max int, changeHandler func(int)) {
selectedString := formatter(selectedValue)
labelValue := fmt.Sprintf(selectedString, selectedValue)
if readOnly {
imgui.LabelText(label, labelValue)
} else if gui.StepSliderIntV(label, &selectedValue, min, max, selectedString) {
changeHandler(selectedValue)
}
}
func (view *ControlView) requestSetZShift(lvl *level.Level, newValue int) {
lvl.SetHeightShift(level.HeightShift(newValue))
view.patchLevelResources(lvl, func() {})
}
func (view *ControlView) requestSetLevelTexture(lvl *level.Level, atlasIndex, worldTextureIndex int) {
lvl.SetTextureAtlasEntry(atlasIndex, level.TextureIndex(worldTextureIndex))
view.patchLevelResources(lvl, func() {
view.model.selectedAtlasIndex = atlasIndex
})
}
func (view *ControlView) requestSetSurveillanceSource(lvl *level.Level, objectIndex int, objectID level.ObjectID) {
lvl.SetSurveillanceSource(objectIndex, objectID)
view.patchLevelResources(lvl, func() {
view.model.selectedSurveillanceObjectIndex = objectIndex
})
}
func (view *ControlView) requestSetSurveillanceSurrogate(lvl *level.Level, objectIndex int, objectID level.ObjectID) {
lvl.SetSurveillanceSurrogate(objectIndex, objectID)
view.patchLevelResources(lvl, func() {
view.model.selectedSurveillanceObjectIndex = objectIndex
})
}
func (view *ControlView) requestSetCeilingHazard(lvl *level.Level, info ceilingHazardInfo) {
parameters := lvl.Parameters()
parameters.RadiationRegister = 0
if info.radiationRegister {
parameters.RadiationRegister = 2
}
view.patchLevelResources(lvl, func() {})
}
func (view *ControlView) requestSetCeilingHazardLevel(lvl *level.Level, value byte) {
lvl.Parameters().CeilingHazardLevel = value
view.patchLevelResources(lvl, func() {})
}
func (view *ControlView) requestSetFloorHazard(lvl *level.Level, info floorHazardInfo) {
parameters := lvl.Parameters()
parameters.BiohazardRegister = 0
parameters.FloorHazardIsGravity = 0
if info.isGravity {
parameters.FloorHazardIsGravity = 1
} else if info.biohazardRegister {
parameters.BiohazardRegister = 2
}
view.patchLevelResources(lvl, func() {})
}
func (view *ControlView) requestSetFloorHazardLevel(lvl *level.Level, value byte) {
lvl.Parameters().FloorHazardLevel = value
view.patchLevelResources(lvl, func() {})
}
func (view *ControlView) requestSetTextureAnimationTime(lvl *level.Level, index int, value uint16) {
lvl.TextureAnimations()[index].FrameTime = value
view.patchLevelResources(lvl, func() {
view.model.selectedTextureAnimationIndex = index
})
}
func (view *ControlView) requestSetTextureAnimationFrameCount(lvl *level.Level, index int, value byte) {
lvl.TextureAnimations()[index].FrameCount = value
view.patchLevelResources(lvl, func() {
view.model.selectedTextureAnimationIndex = index
})
}
func (view *ControlView) requestSetTextureAnimationType(lvl *level.Level, index int, value level.TextureAnimationLoopType) {
lvl.TextureAnimations()[index].LoopType = value
view.patchLevelResources(lvl, func() {
view.model.selectedTextureAnimationIndex = index
})
}
func (view *ControlView) patchLevelResources(lvl *level.Level, extraRestoreState func()) {
oldLevelID := view.levelSelection.CurrentLevelID()
extraTask := func(world.Modder) error {
extraRestoreState()
return nil
}
err := view.registry.Register(cmd.Named("PatchLevel"),
cmd.Forward(view.restoreFocusTask()),
cmd.Forward(view.levelSelection.SetCurrentLevelIDTask(lvl.ID())),
cmd.Forward(extraTask),
cmd.Nested(func() error { return view.levels.CommitLevelChanges(lvl.ID()) }),
cmd.Reverse(extraTask),
cmd.Reverse(view.levelSelection.SetCurrentLevelIDTask(oldLevelID)),
cmd.Reverse(view.restoreFocusTask()))
if err != nil {
panic(err)
}
}
func (view *ControlView) restoreFocusTask() cmd.Task {
return func(modder world.Modder) error {
view.model.restoreFocus = true
return nil
}
}