Moving some directories
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user