-
Notifications
You must be signed in to change notification settings - Fork 7
/
EditableLevels.go
113 lines (100 loc) · 3.75 KB
/
EditableLevels.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package edit
import (
"github.com/inkyblackness/hacked/ss1/content/archive"
"github.com/inkyblackness/hacked/ss1/content/archive/level"
"github.com/inkyblackness/hacked/ss1/content/archive/level/lvlids"
"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"
)
// EditableLevels is a list of levels that can be modified.
type EditableLevels struct {
registry cmd.Registry
mod *world.Mod
list [archive.MaxLevels]*level.Level
}
// NewEditableLevels returns a new instance for given mod.
func NewEditableLevels(registry cmd.Registry, mod *world.Mod) *EditableLevels {
var levels EditableLevels
levels.registry = registry
levels.mod = mod
for i := 0; i < len(levels.list); i++ {
levels.list[i] = level.NewLevel(ids.LevelResourcesStart, i, mod)
}
return &levels
}
// InvalidateResources informs all levels of changed resource identifier.
func (levels *EditableLevels) InvalidateResources(modifiedIDs []resource.ID) {
for _, lvl := range levels.list {
lvl.InvalidateResources(modifiedIDs)
}
}
// Level returns the pointer to the given level. The function panics if the index is invalid.
func (levels *EditableLevels) Level(index int) *level.Level {
if !levels.IsLevelAvailable(index) {
panic("Invalid level index")
}
return levels.list[index]
}
// IsLevelReadOnly returns true if the identified level cannot be modified.
func (levels *EditableLevels) IsLevelReadOnly(index int) bool {
isInMod := len(levels.mod.ModifiedBlocks(resource.LangAny, ids.LevelResourcesStart.Plus(lvlids.PerLevel*index+lvlids.FirstUsed))) > 0
return !levels.IsLevelAvailable(index) || !isInMod
}
// IsLevelAvailable returns true if the given index is a valid level.
func (levels *EditableLevels) IsLevelAvailable(index int) bool {
return (index >= 0) && (index < len(levels.list))
}
// IsObjectInUse returns true if the given object ID is in use in given level.
func (levels *EditableLevels) IsObjectInUse(levelIndex int, id level.ObjectID) bool {
if !levels.IsLevelAvailable(levelIndex) {
return false
}
lvl := levels.list[levelIndex]
obj := lvl.Object(id)
return (obj != nil) && (obj.InUse != 0)
}
// IsTileOnMap returns true if the given tile position points to a valid tile in the given level.
func (levels *EditableLevels) IsTileOnMap(levelIndex int, pos level.TilePosition) bool {
if !levels.IsLevelAvailable(levelIndex) {
return false
}
lvl := levels.list[levelIndex]
return lvl.Tile(pos) != nil
}
// CommitLevelChanges applies any level data change compared to the underlying mod.
func (levels *EditableLevels) CommitLevelChanges(index int) error {
lvl := levels.Level(index)
newDataSet := lvl.EncodeState()
var patches []world.BlockPatch
for id, newData := range &newDataSet {
if len(newData) > 0 {
resourceID := ids.LevelResourcesStart.Plus(lvlids.PerLevel*lvl.ID() + id)
patch, changed, err := levels.mod.CreateBlockPatch(resource.LangAny, resourceID, 0, newData)
if err != nil {
return err
} else if changed {
patches = append(patches, patch)
}
}
}
return levels.registry.Register(cmd.Named("CommitLevelChanges"),
cmd.Forward(forwardTask(patches)), cmd.Reverse(reverseTask(patches)))
}
func forwardTask(patches []world.BlockPatch) cmd.Task {
return func(modder world.Modder) error {
for _, patch := range patches {
modder.PatchResourceBlock(resource.LangAny, patch.ID, patch.BlockIndex, patch.BlockLength, patch.ForwardData)
}
return nil
}
}
func reverseTask(patches []world.BlockPatch) cmd.Task {
return func(modder world.Modder) error {
for _, patch := range patches {
modder.PatchResourceBlock(resource.LangAny, patch.ID, patch.BlockIndex, patch.BlockLength, patch.ReverseData)
}
return nil
}
}