Files
questshock/modding/hacked/ss1/edit/LevelSelectionService.go
T

232 lines
7.4 KiB
Go

package edit
import (
"sort"
"github.com/inkyblackness/hacked/ss1/content/archive/level"
"github.com/inkyblackness/hacked/ss1/edit/undoable/cmd"
"github.com/inkyblackness/hacked/ss1/world"
)
type levelSelection struct {
tiles map[level.TilePosition]struct{}
objects map[level.ObjectID]struct{}
}
func (selection levelSelection) tileList() []level.TilePosition {
list := make([]level.TilePosition, 0, len(selection.tiles))
for pos := range selection.tiles {
list = append(list, pos)
}
sort.Slice(list, func(a, b int) bool {
posA := list[a]
posB := list[b]
return (posA.Y < posB.Y) || ((posA.Y == posB.Y) && (posA.X < posB.X))
})
return list
}
func (selection levelSelection) objectList() []level.ObjectID {
list := make([]level.ObjectID, 0, len(selection.objects))
for id := range selection.objects {
list = append(list, id)
}
sort.Slice(list, func(a, b int) bool { return list[a] < list[b] })
return list
}
// LevelInfoProvider returns details on level content.
type LevelInfoProvider interface {
IsLevelAvailable(index int) bool
IsObjectInUse(levelIndex int, id level.ObjectID) bool
IsTileOnMap(levelIndex int, pos level.TilePosition) bool
}
// LevelSelectionService keeps track of selections related to levels.
type LevelSelectionService struct {
provider LevelInfoProvider
currentLevel int
levels map[int]*levelSelection
}
// NewLevelSelectionService returns a new instance.
func NewLevelSelectionService(provider LevelInfoProvider) *LevelSelectionService {
return &LevelSelectionService{
provider: provider,
currentLevel: 0,
levels: make(map[int]*levelSelection),
}
}
// CurrentLevelID returns the identifier for the currently selected level.
func (service *LevelSelectionService) CurrentLevelID() int {
if !service.provider.IsLevelAvailable(service.currentLevel) {
return 0
}
return service.currentLevel
}
// SetCurrentLevelID sets the currently selected level.
func (service *LevelSelectionService) SetCurrentLevelID(index int) {
service.currentLevel = index
}
// SetCurrentLevelIDTask returns a command task that will set the current level.
func (service *LevelSelectionService) SetCurrentLevelIDTask(index int) cmd.Task {
return func(modder world.Modder) error {
service.SetCurrentLevelID(index)
return nil
}
}
func (service *LevelSelectionService) currentSelection() levelSelection {
selection, ok := service.levels[service.currentLevel]
if !ok {
return levelSelection{}
}
return *service.cleanSelection(service.currentLevel, selection)
}
func (service *LevelSelectionService) modifiableSelection() *levelSelection {
selection, ok := service.levels[service.currentLevel]
if !ok {
selection = &levelSelection{
tiles: make(map[level.TilePosition]struct{}),
objects: make(map[level.ObjectID]struct{}),
}
service.levels[service.currentLevel] = selection
}
return service.cleanSelection(service.currentLevel, selection)
}
func (service *LevelSelectionService) cleanSelection(levelIndex int, selection *levelSelection) *levelSelection {
return service.cleanSelectionTiles(levelIndex, service.cleanSelectionObjects(levelIndex, selection))
}
func (service *LevelSelectionService) cleanSelectionTiles(levelIndex int, selection *levelSelection) *levelSelection {
var toDelete []level.TilePosition
for pos := range selection.tiles {
if !service.provider.IsTileOnMap(levelIndex, pos) {
toDelete = append(toDelete, pos)
}
}
for _, pos := range toDelete {
delete(selection.tiles, pos)
}
return selection
}
func (service *LevelSelectionService) cleanSelectionObjects(levelIndex int, selection *levelSelection) *levelSelection {
var toDelete []level.ObjectID
for id := range selection.objects {
if !service.provider.IsObjectInUse(levelIndex, id) {
toDelete = append(toDelete, id)
}
}
for _, id := range toDelete {
delete(selection.objects, id)
}
return selection
}
// IsTileSelected returns true if the given tile is currently selected.
func (service *LevelSelectionService) IsTileSelected(pos level.TilePosition) bool {
_, selected := service.currentSelection().tiles[pos]
return selected
}
// CurrentSelectedTiles returns the list of currently selected tiles in the current level.
func (service *LevelSelectionService) CurrentSelectedTiles() []level.TilePosition {
return service.currentSelection().tileList()
}
// SetCurrentSelectedTiles sets the list of currently selected tiles in the current level.
func (service *LevelSelectionService) SetCurrentSelectedTiles(list []level.TilePosition) {
selection := service.modifiableSelection()
for pos := range selection.tiles {
delete(selection.tiles, pos)
}
for _, pos := range list {
selection.tiles[pos] = struct{}{}
}
}
// AddCurrentSelectedTiles adds the given tile IDs to the list of currently selected in the current level.
func (service *LevelSelectionService) AddCurrentSelectedTiles(list []level.TilePosition) {
selection := service.modifiableSelection()
for _, pos := range list {
selection.tiles[pos] = struct{}{}
}
}
// RemoveCurrentSelectedTiles removes the given tile IDs from the list of currently selected in the current level.
func (service *LevelSelectionService) RemoveCurrentSelectedTiles(list []level.TilePosition) {
selection := service.modifiableSelection()
for _, pos := range list {
delete(selection.tiles, pos)
}
}
// ToggleTileSelection toggles the selection of the given tile IDs in the current level.
func (service *LevelSelectionService) ToggleTileSelection(list []level.TilePosition) {
selection := service.modifiableSelection()
for _, pos := range list {
if _, selected := selection.tiles[pos]; selected {
delete(selection.tiles, pos)
} else {
selection.tiles[pos] = struct{}{}
}
}
}
// NumberOfSelectedObjects returns the number of currently selected objects in the current level.
func (service *LevelSelectionService) NumberOfSelectedObjects() int {
return len(service.currentSelection().objects)
}
// CurrentSelectedObjects returns the list of currently selected objects in the current level.
func (service *LevelSelectionService) CurrentSelectedObjects() []level.ObjectID {
return service.currentSelection().objectList()
}
// SetCurrentSelectedObjects sets the list of currently selected objects in the current level.
func (service *LevelSelectionService) SetCurrentSelectedObjects(ids []level.ObjectID) {
selection := service.modifiableSelection()
for id := range selection.objects {
delete(selection.objects, id)
}
for _, id := range ids {
selection.objects[id] = struct{}{}
}
}
// AddCurrentSelectedObjects adds the given object IDs to the list of currently selected in the current level.
func (service *LevelSelectionService) AddCurrentSelectedObjects(ids []level.ObjectID) {
selection := service.modifiableSelection()
for _, id := range ids {
selection.objects[id] = struct{}{}
}
}
// RemoveCurrentSelectedObjects removes the given object IDs from the list of currently selected in the current level.
func (service *LevelSelectionService) RemoveCurrentSelectedObjects(ids []level.ObjectID) {
selection := service.modifiableSelection()
for _, id := range ids {
delete(selection.objects, id)
}
}
// ToggleObjectSelection toggles the selection of the given object IDs in the current level.
func (service *LevelSelectionService) ToggleObjectSelection(ids []level.ObjectID) {
selection := service.modifiableSelection()
for _, id := range ids {
if _, selected := selection.objects[id]; selected {
delete(selection.objects, id)
} else {
selection.objects[id] = struct{}{}
}
}
}