diff --git a/editor/levels/ObjectsView.go b/editor/levels/ObjectsView.go index a791b93..e6ed175 100644 --- a/editor/levels/ObjectsView.go +++ b/editor/levels/ObjectsView.go @@ -647,6 +647,223 @@ func (view *ObjectsView) renderPropertyControl(lvl *level.Level, readOnly bool, }) }) + simplifier.SetSpecialHandler("MultiAnimation", func() { + /* + animationTypes := []string{"Random"} + selectedType := -1 + if unifier.IsUnique() { + value := int(unifier.Unified().(int32)) + selectedType = value >> 12 + } + + values.RenderUnifiedCombo(readOnly, key+"###"+fullKey+"-Type", unifier, + func(u values.Unifier) int { + key := unifier.Unified().(int32) + return int(key >> 12) + }, + func(index int) string { return animationTypes[index] }, + len(animationTypes), + func(newIndex int) { + updater(func(oldValue uint32) uint32 { + result := oldValue & ^uint32(0xF000) + result |= uint32(newIndex<<12) & uint32(0xF000) + return result + }) + }) + */ + + // if selectedType == 0 { + values.RenderUnifiedSliderInt(readOnly, key+" infrequency###"+fullKey+"-infrequency", unifier, + func(u values.Unifier) int { + key := unifier.Unified().(int32) + return int(key & 0x0FFF) + }, + func(value int) string { return "%d" }, + 0, 0x0FFF, + func(newValue int) { + updater(func(oldValue uint32) uint32 { + result := oldValue & ^uint32(0x0FFF) + result |= uint32(newValue) & uint32(0x0FFF) + return result + }) + }) + //} + }) + + simplifier.SetSpecialHandler("PictureSource", func() { + indexConverter := func(u values.Unifier) int { return int(u.Unified().(int32)) & 0x007F } + indexUpdater := func(newValue int) { + updater(func(oldValue uint32) uint32 { + result := oldValue & ^uint32(0x007F) + result |= uint32(newValue) & uint32(0x007F) + return result + }) + } + + sourceType := []string{"Data", "ObjectID"} + selectedType := -1 + if unifier.IsUnique() { + value := int(unifier.Unified().(int32)) + selectedType = (value & 0x1000) >> 12 + } + + imgui.Separator() + imgui.Text(key + ":") + + values.RenderUnifiedCombo(readOnly, "Source###"+fullKey+"-Source", unifier, + func(u values.Unifier) int { + key := unifier.Unified().(int32) + return int((key & 0x1000) >> 12) + }, + func(index int) string { return sourceType[index] }, + len(sourceType), + func(newIndex int) { + updater(func(oldValue uint32) uint32 { + result := oldValue & ^uint32(0x1000) + result |= uint32(newIndex<<12) & uint32(0x1000) + return result + }) + }) + + if selectedType == 0 { + dataType := []string{"Screen", "Custom", "Text", "Scrolling Text"} + textureData := -1 + index := -1 + selectedDataType := -1 + + if unifier.IsUnique() { + value := int(unifier.Unified().(int32)) + textureData = value & 0x0FFF + index = textureData & 0x007F + selectedDataType = (value & 0x0180) >> 7 + } + + values.RenderUnifiedCombo(readOnly, "Type###"+fullKey+"-Type", unifier, + func(u values.Unifier) int { + key := unifier.Unified().(int32) + return int((key & 0x0180) >> 7) + }, + func(index int) string { return dataType[index] }, + len(dataType), + func(newIndex int) { + updater(func(oldValue uint32) uint32 { + result := oldValue & ^uint32(0x0180) + result |= uint32(newIndex<<7) & uint32(0x0180) + return result + }) + }) + + if selectedDataType == 0 { // Screen + resInfo, _ := ids.Info(ids.ScreenTextures) + + values.RenderUnifiedSliderInt(readOnly, "Index###"+fullKey+"-Index", unifier, + indexConverter, + func(value int) string { return "%d" }, + 0, resInfo.MaxCount-1, + indexUpdater) + + render.TextureSelector(key+"-Screen###"+fullKey+"-Screen", -1, view.guiScale, resInfo.MaxCount, index, + view.textureCache, + func(index int) resource.Key { + return resource.KeyOf(ids.ScreenTextures.Plus(index), resource.LangAny, 0) + }, + func(index int) string { return fmt.Sprintf("%3d", index) }, + func(index int) { + if !readOnly { + indexUpdater(index) + } + }) + } else if selectedDataType == 1 { // Custom + values.RenderUnifiedSliderInt(readOnly, "Index###"+fullKey+"-Index", unifier, + indexConverter, + func(value int) string { + if value >= 0x78 && value <= (0x78+8) { + return "%d - Camera" + } else if value == 0x77 { + return "%d - Static" + } else if value == 0x76 { + return "%d - SHODAN Proximity" + } else if value >= 0x70 && value <= (0x70+6) { + return "%d - Automap" + } else { + return "%d" + } + }, + 0, 0x007F, + indexUpdater) + + resInfo, _ := ids.Info(ids.ObjectMaterialBitmaps) + if index < resInfo.MaxCount { + render.TextureImage("Preview", view.textureCache, resource.KeyOf(ids.ObjectMaterialBitmaps.Plus(index), resource.LangAny, 0), imgui.Vec2{X: 64 * view.guiScale, Y: 64 * view.guiScale}) + } + } else if selectedDataType == 2 { // Text + values.RenderUnifiedSliderInt(readOnly, "Index###"+fullKey+"-Index", unifier, + indexConverter, + func(value int) string { + if value == 0x007F { + return "%d - (Random number)" + } else { + return "%d" + } + }, + 0, 0x007F, + indexUpdater) + } else if selectedDataType == 3 { // Scrolling Text + values.RenderUnifiedSliderInt(readOnly, "Index###"+fullKey+"-Index", unifier, + indexConverter, + func(value int) string { return "%d" }, + 0, 0x007F, + indexUpdater) + } + + if selectedDataType == 2 || selectedDataType == 3 { + fontNames := []string{"Large Tech", "Small Tech"} + values.RenderUnifiedCombo(readOnly, "Font###"+fullKey+"-Font", unifier, + func(u values.Unifier) int { + key := unifier.Unified().(int32) + return int((key & 0x0800) >> 11) + }, + func(value int) string { return fontNames[value] }, + len(fontNames), + func(newValue int) { + updater(func(oldValue uint32) uint32 { + result := oldValue & ^uint32(0x0800) + result |= uint32(newValue<<11) & uint32(0x0800) + return result + }) + }) + } + + values.RenderUnifiedSliderInt(readOnly, "Scale###"+fullKey+"-Scale", unifier, + func(u values.Unifier) int { + key := unifier.Unified().(int32) + return int((key & 0x0600) >> 9) + }, + func(value int) string { return "%d" }, + 0, 3, + func(newValue int) { + updater(func(oldValue uint32) uint32 { + result := oldValue & ^uint32(0x0600) + result |= uint32(newValue<<9) & uint32(0x0600) + return result + }) + }) + } else { + // Indirection to another object + values.RenderUnifiedSliderInt(readOnly, "ObjectID###"+fullKey+"-Object", unifier, + func(u values.Unifier) int { return int(u.Unified().(int32)) & 0x0FFF }, + func(value int) string { return "%d" }, + 0, lvl.ObjectCapacity(), + func(newValue int) { + updater(func(oldValue uint32) uint32 { + result := oldValue & ^uint32(0x0FFF) + result |= uint32(newValue) & uint32(0x0FFF) + return result + }) + }) + } + }) + describer(simplifier) } diff --git a/editor/levels/TilesView.go b/editor/levels/TilesView.go index 42d9801..bb2c090 100644 --- a/editor/levels/TilesView.go +++ b/editor/levels/TilesView.go @@ -93,7 +93,7 @@ func (view *TilesView) renderContent(lvl *level.Level, tiles []*level.TileMapEnt ceilingHeightUnifier := values.NewUnifier() slopeHeightUnifier := values.NewUnifier() slopeControlUnifier := values.NewUnifier() - musicIndexUnifier := values.NewUnifier() + musicZoneUnifier := values.NewUnifier() floorPaletteIndexUnifier := values.NewUnifier() ceilingPaletteIndexUnifier := values.NewUnifier() @@ -113,6 +113,7 @@ func (view *TilesView) renderContent(lvl *level.Level, tiles []*level.TileMapEnt ceilingLightUnifier := values.NewUnifier() ceilingLightDeltaUnifier := values.NewUnifier() deconstructedUnifier := values.NewUnifier() + perildUnifier := values.NewUnifier() floorHazardUnifier := values.NewUnifier() ceilingHazardUnifier := values.NewUnifier() @@ -122,7 +123,8 @@ func (view *TilesView) renderContent(lvl *level.Level, tiles []*level.TileMapEnt ceilingHeightUnifier.Add(tile.Ceiling.AbsoluteHeight()) slopeHeightUnifier.Add(tile.SlopeHeight) slopeControlUnifier.Add(tile.Flags.SlopeControl()) - musicIndexUnifier.Add(tile.Flags.MusicIndex()) + musicZoneUnifier.Add(tile.Flags.MusicZone()) + perildUnifier.Add(tile.Flags.Peril()) if isCyberspace { floorPaletteIndexUnifier.Add(tile.TextureInfo.FloorPaletteIndex()) ceilingPaletteIndexUnifier.Add(tile.TextureInfo.CeilingPaletteIndex()) @@ -180,11 +182,14 @@ func (view *TilesView) renderContent(lvl *level.Level, tiles []*level.TileMapEnt func(value int) string { return slopeControls[value].String() }, len(slopeControls), func(newValue int) { view.changeTiles(setSlopeControlTo(slopeControls[newValue])) }) - values.RenderUnifiedSliderInt(readOnly, "Music Index", musicIndexUnifier, - func(u values.Unifier) int { return u.Unified().(int) }, - func(value int) string { return "%d" }, - 0, 15, - func(newValue int) { view.changeTiles(setMusicIndexTo(newValue)) }) + musicZones := level.TileMusicZones() + values.RenderUnifiedCombo(readOnly, "Music Zone", musicZoneUnifier, + func(u values.Unifier) int { return int(u.Unified().(level.TileMusicZone)) }, + func(value int) string { return musicZones[value].String() }, + len(musicZones), + func(newValue int) { view.changeTiles(setMusicZoneTo(musicZones[newValue])) }) + values.RenderUnifiedCheckboxCombo(readOnly, "Music Peril", perildUnifier, + func(newValue bool) { view.changeTiles(setPerilTo(newValue)) }) imgui.Separator() @@ -400,9 +405,15 @@ func setSlopeControlTo(value level.TileSlopeControl) tileMapEntryModifier { } } -func setMusicIndexTo(value int) tileMapEntryModifier { +func setPerilTo(value bool) tileMapEntryModifier { return func(tile *level.TileMapEntry) { - tile.Flags = tile.Flags.WithMusicIndex(value) + tile.Flags = tile.Flags.WithPeril(value) + } +} + +func setMusicZoneTo(value level.TileMusicZone) tileMapEntryModifier { + return func(tile *level.TileMapEntry) { + tile.Flags = tile.Flags.WithMusicZone(value) } } diff --git a/ss1/content/archive/level/TileFlag.go b/ss1/content/archive/level/TileFlag.go index 866cccc..ab82868 100644 --- a/ss1/content/archive/level/TileFlag.go +++ b/ss1/content/archive/level/TileFlag.go @@ -1,5 +1,7 @@ package level +import "fmt" + // TileFlag describes simple properties of a map tile. type TileFlag uint32 @@ -19,17 +21,28 @@ func (flag TileFlag) ForCyberspace() CyberspaceFlag { return CyberspaceFlag(flag) } -// MusicIndex returns the music identifier. Range: [0..15]. -func (flag TileFlag) MusicIndex() int { - return int((flag & 0x0000F000) >> 12) +// MusicZone returns the music zone. +func (flag TileFlag) MusicZone() TileMusicZone { + return TileMusicZone((flag & 0x0000E000) >> 13) } -// WithMusicIndex returns a new flag value with the given music index set. Values beyond allowed range are ignored. -func (flag TileFlag) WithMusicIndex(value int) TileFlag { - if (value < 0) || (value > 15) { - return flag +// WithMusicZone returns a new flag value with the given music index set. +func (flag TileFlag) WithMusicZone(value TileMusicZone) TileFlag { + return TileFlag(uint32(flag&^0x0000E000) | (uint32(value) << 13)) +} + +// Peril returns whether the tile is marked as peril (may play peril music). +func (flag TileFlag) Peril() bool { + return (flag & 0x00001000) != 0 +} + +// WithPeril returns a flag with the given peril set. +func (flag TileFlag) WithPeril(value bool) TileFlag { + var valueFlag uint32 + if value { + valueFlag = 0x00001000 } - return TileFlag(uint32(flag&^0x0000F000) | (uint32(value) << 12)) + return TileFlag(uint32(flag&^0x00001000) | valueFlag) } // SlopeControl returns the slope control as per flags. @@ -126,7 +139,7 @@ func (flag RealWorldFlag) TileVisited() bool { return (flag & 0x80000000) != 0 } -// WithTileVisited returns a flag with the given deconstruction set. +// WithTileVisited returns a flag with the given visited set. func (flag RealWorldFlag) WithTileVisited(value bool) RealWorldFlag { var valueFlag uint32 if value { @@ -162,3 +175,53 @@ func (flag CyberspaceFlag) WithFlightPull(value CyberspaceFlightPull) Cyberspace newFlag |= uint32(value&0x10) << 20 return CyberspaceFlag(newFlag) } + +type TileMusicZone byte + +const ( + TileMusicZoneNoMusic TileMusicZone = 0 + TileMusicZoneHospital TileMusicZone = 1 + TileMusicZoneExecutive TileMusicZone = 2 + TileMusicZoneIndustrial TileMusicZone = 3 + TileMusicZoneMetal TileMusicZone = 4 + TileMusicZonePark TileMusicZone = 5 + TileMusicZoneBridge TileMusicZone = 6 + TileMusicZoneElevator TileMusicZone = 7 +) + +// TileSlopeControls returns all control values. +func TileMusicZones() []TileMusicZone { + return []TileMusicZone{ + TileMusicZoneNoMusic, + TileMusicZoneHospital, + TileMusicZoneExecutive, + TileMusicZoneIndustrial, + TileMusicZoneMetal, + TileMusicZonePark, + TileMusicZoneBridge, + TileMusicZoneElevator, + } +} + +func (ctrl TileMusicZone) String() string { + switch ctrl { + case TileMusicZoneNoMusic: + return "No Music" + case TileMusicZoneHospital: + return "Hospital" + case TileMusicZoneExecutive: + return "Executive" + case TileMusicZoneIndustrial: + return "Industrial" + case TileMusicZoneMetal: + return "Metal" + case TileMusicZonePark: + return "Park" + case TileMusicZoneBridge: + return "Bridge" + case TileMusicZoneElevator: + return "Elevator" + default: + return fmt.Sprintf("Unknown%02X", int(ctrl)) + } +} diff --git a/ss1/content/archive/level/lvlobj/BigStuff.go b/ss1/content/archive/level/lvlobj/BigStuff.go index a76b2e5..f52d97e 100644 --- a/ss1/content/archive/level/lvlobj/BigStuff.go +++ b/ss1/content/archive/level/lvlobj/BigStuff.go @@ -6,26 +6,28 @@ import ( var baseBigStuff = interpreters.New() +var multiAnimation = interpreters.New(). + With("LoopType", 0, 2).As(interpreters.Bitfield(map[uint32]string{0x01: "Forward/Backward", 0x02: "Backward"})). + With("Alternation", 2, 2).As(interpreters.SpecialValue("MultiAnimation")). + With("Picture", 4, 2).As(interpreters.SpecialValue("PictureSource")). + With("Alternate", 6, 2).As(interpreters.SpecialValue("PictureSource")) + var displayScenery = baseBigStuff. - With("FrameCount", 0, 2).As(interpreters.RangedValue(0, 4)). - With("LoopType", 2, 2).As(interpreters.EnumValue(map[uint32]string{0: "Forward", 1: "Forward/Backward", 2: "Backward", 3: "Forward/Backward"})). - With("AlternationType", 4, 2).As(interpreters.EnumValue(map[uint32]string{0: "Don't Alternate", 3: "Alternate Randomly"})). - With("PictureSource", 6, 2).As(interpreters.RangedValue(0, 0x01FF)). - With("AlternateSource", 8, 2).As(interpreters.RangedValue(0, 0x01FF)) + With("FrameCount", 0, 2).As(interpreters.RangedValue(0, 8)). + Refining("", 2, 8, multiAnimation, interpreters.Always) var displayControlPedestal = baseBigStuff. - With("FrameCount", 0, 2).As(interpreters.RangedValue(0, 4)). - With("TriggerObjectID", 2, 2).As(interpreters.ObjectID()). - With("AlternationType", 4, 2).As(interpreters.EnumValue(map[uint32]string{0: "Don't Alternate", 3: "Alternate Randomly"})). - With("PictureSource", 6, 2).As(interpreters.RangedValue(0, 0x01FF)). - With("AlternateSource", 8, 2).As(interpreters.RangedValue(0, 0x01FF)) + With("FrameCount", 0, 2).As(interpreters.RangedValue(0, 8)). + With("TriggerObjectID1", 2, 2).As(interpreters.ObjectID()). + With("TriggerObjectID2", 4, 2).As(interpreters.ObjectID()). + Refining("", 2, 8, multiAnimation, interpreters.Always) var cabinetFurniture = baseBigStuff. With("Object1ID", 2, 2).As(interpreters.ObjectID()). With("Object2ID", 4, 2).As(interpreters.ObjectID()) var texturableFurniture = baseBigStuff. - With("TextureIndex", 6, 2).As(interpreters.RangedValue(0, 500)) + With("Texture", 6, 2).As(interpreters.SpecialValue("PictureSource")) var wordScenery = baseBigStuff. With("TextIndex", 0, 2).As(interpreters.RangedValue(0, 511)). @@ -38,7 +40,8 @@ var textureMapScenery = baseBigStuff. With("TextureIndex", 6, 2).As(interpreters.SpecialValue("LevelTexture")) var buttonControlPedestal = baseBigStuff. - With("TriggerObjectID", 2, 2).As(interpreters.ObjectID()) + With("TriggerObjectID1", 2, 2).As(interpreters.ObjectID()). + With("TriggerObjectID2", 4, 2).As(interpreters.ObjectID()) var surgicalMachine = baseBigStuff. With("BrokenState", 2, 1).As(interpreters.EnumValue(map[uint32]string{0x00: "OK", 0xE7: "Broken"})). @@ -91,6 +94,7 @@ func initBigStuff() interpreterRetriever { scienceSecurityEquipment := newInterpreterEntry(baseBigStuff) scienceSecurityEquipment.set(4, newInterpreterLeaf(securityCamera)) + scienceSecurityEquipment.set(5, newInterpreterLeaf(buttonControlPedestal)) scienceSecurityEquipment.set(6, newInterpreterLeaf(displayControlPedestal)) gardenScenery := newInterpreterEntry(baseBigStuff)