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

273 lines
8.7 KiB
Go

package edit
import (
"fmt"
"github.com/inkyblackness/hacked/ss1/content/archive"
"github.com/inkyblackness/hacked/ss1/edit/undoable/cmd"
"github.com/inkyblackness/hacked/ss1/world"
"github.com/inkyblackness/hacked/ss1/world/citadel"
)
// VariableBaseContextIdentifier identifies which context for variable naming should be used.
type VariableBaseContextIdentifier int
// Context identifier are listed below.
const (
VariableContextCitadel = VariableBaseContextIdentifier(0)
VariableContextEngine = VariableBaseContextIdentifier(1)
)
// VariableContextIdentifiers returns a list of all available identifiers.
func VariableContextIdentifiers() []VariableBaseContextIdentifier {
return []VariableBaseContextIdentifier{VariableContextCitadel, VariableContextEngine}
}
// GameStateSettings describe the game state details.
type GameStateSettings struct {
BaseContext int
BooleanVariables []VariableSettings
IntegerVariables []VariableSettings
}
// VariableSettings describe game variables.
type VariableSettings struct {
Index int
ResetValue *int16 `json:",omitempty"`
Name string
Description string
Limits *struct {
Minimum int16
Maximum int16
} `json:",omitempty"`
ValueNames []struct {
Value int16
Name string
} `json:",omitempty"`
}
func variableSettingsFrom(variables archive.GameVariables) []VariableSettings {
settings := make([]VariableSettings, 0, len(variables))
for varIndex, varInfo := range variables {
varSettings := VariableSettings{
Index: varIndex,
ResetValue: varInfo.InitValue,
Name: varInfo.Name,
Description: varInfo.Description,
Limits: nil,
ValueNames: nil,
}
if varInfo.Limits != nil {
varSettings.Limits = &struct {
Minimum int16
Maximum int16
}{
Minimum: varInfo.Limits.Minimum,
Maximum: varInfo.Limits.Maximum,
}
}
for valIndex, valName := range varInfo.ValueNames {
varSettings.ValueNames = append(varSettings.ValueNames,
struct {
Value int16
Name string
}{
Value: valIndex,
Name: valName,
})
}
settings = append(settings, varSettings)
}
return settings
}
func variablesFrom(list []VariableSettings) archive.GameVariables {
variables := make(archive.GameVariables)
for _, settings := range list {
varInfo := archive.GameVariableInfo{
InitValue: settings.ResetValue,
Name: settings.Name,
Description: settings.Description,
}
if settings.Limits != nil {
varInfo.Limits = &archive.GameVariableLimits{
Minimum: settings.Limits.Maximum,
Maximum: settings.Limits.Minimum,
}
}
if len(settings.ValueNames) > 0 {
varInfo.ValueNames = make(map[int16]string)
for _, valName := range settings.ValueNames {
varInfo.ValueNames[valName.Value] = valName.Name
}
}
variables[settings.Index] = varInfo
}
return variables
}
// GameStateService handles all details on the global game state.
type GameStateService struct {
registry cmd.Registry
currentContext VariableBaseContextIdentifier
booleanVariables archive.GameVariables
integerVariables archive.GameVariables
}
// NewGameStateService returns a new instance.
func NewGameStateService(registry cmd.Registry) *GameStateService {
return &GameStateService{
registry: registry,
currentContext: VariableContextCitadel,
booleanVariables: make(archive.GameVariables),
integerVariables: make(archive.GameVariables),
}
}
// CurrentSettings returns the snapshot of the project.
func (service GameStateService) CurrentSettings() GameStateSettings {
return GameStateSettings{
BaseContext: int(service.currentContext),
BooleanVariables: variableSettingsFrom(service.booleanVariables),
IntegerVariables: variableSettingsFrom(service.integerVariables),
}
}
// RestoreSettings loads the given snapshot.
func (service *GameStateService) RestoreSettings(settings GameStateSettings) {
service.currentContext = VariableBaseContextIdentifier(settings.BaseContext)
service.booleanVariables = variablesFrom(settings.BooleanVariables)
service.integerVariables = variablesFrom(settings.IntegerVariables)
}
// VariableBaseContext returns the current context to use as basis for variables not specified by the project.
func (service GameStateService) VariableBaseContext() VariableBaseContextIdentifier {
return service.currentContext
}
// SetVariableBaseContext sets the current context to use as basis for variables not specified by the project.
func (service *GameStateService) SetVariableBaseContext(identifier VariableBaseContextIdentifier) {
service.currentContext = identifier
}
func (service GameStateService) baseInfoProvider() archive.GameVariableInfoProvider {
switch service.currentContext {
case VariableContextCitadel:
return citadel.MissionVariables{}
case VariableContextEngine:
return archive.EngineVariables{}
default:
panic(fmt.Sprintf("invalid identifier: %d", service.currentContext))
}
}
// DefaultAllVariables removes all project specific overrides.
func (service *GameStateService) DefaultAllVariables() error {
return service.registry.Register(cmd.Named("RemoveAllOverrides"),
cmd.Nested(func() error {
for i := 0; i < archive.BooleanVarCount; i++ {
err := service.DefaultBooleanVariable(i)
if err != nil {
return err
}
}
for i := 0; i < archive.IntegerVarCount; i++ {
err := service.DefaultIntegerVariable(i)
if err != nil {
return err
}
}
return nil
}))
}
// IntegerVariableOverride returns true if project-specific details are stored for given variable index.
func (service GameStateService) IntegerVariableOverride(index int) bool {
_, perProject := service.integerVariables[index]
return perProject
}
// IntegerVariable returns the variable info as per project settings for the given index.
func (service GameStateService) IntegerVariable(index int) archive.GameVariableInfo {
baseInfo := service.baseInfoProvider().IntegerVariable(index)
projInfo, perProject := service.integerVariables[index]
if perProject {
projInfo.Hardcoded = baseInfo.Hardcoded
return projInfo
}
return baseInfo
}
// SetIntegerVariable sets the variable info for the given index in the project settings.
func (service *GameStateService) SetIntegerVariable(index int, info archive.GameVariableInfo) error {
return service.registry.Register(cmd.Named("SetIntegerVariable"),
cmd.Forward(setVariableTask(service.integerVariables, index, info)),
cmd.Reverse(restoreVariableTask(service.integerVariables, index)))
}
// DefaultIntegerVariable clears the variable info for the given index in the project settings.
func (service *GameStateService) DefaultIntegerVariable(index int) error {
return service.registry.Register(cmd.Named("DefaultIntegerVariable"),
cmd.Forward(deleteVariableTask(service.integerVariables, index)),
cmd.Reverse(restoreVariableTask(service.integerVariables, index)))
}
// BooleanVariableOverride returns true if project-specific details are stored for given variable index.
func (service GameStateService) BooleanVariableOverride(index int) bool {
_, perProject := service.booleanVariables[index]
return perProject
}
// BooleanVariable returns the variable info as per project settings for the given index.
func (service GameStateService) BooleanVariable(index int) archive.GameVariableInfo {
baseInfo := service.baseInfoProvider().BooleanVariable(index)
projInfo, perProject := service.booleanVariables[index]
if perProject {
projInfo.Hardcoded = baseInfo.Hardcoded
return projInfo
}
return baseInfo
}
// SetBooleanVariable sets the variable info for the given index in the project settings.
func (service *GameStateService) SetBooleanVariable(index int, info archive.GameVariableInfo) error {
return service.registry.Register(cmd.Named("SetBooleanVariable"),
cmd.Forward(setVariableTask(service.booleanVariables, index, info)),
cmd.Reverse(restoreVariableTask(service.booleanVariables, index)))
}
// DefaultBooleanVariable clears the variable info for the given index in the project settings.
func (service *GameStateService) DefaultBooleanVariable(index int) error {
return service.registry.Register(cmd.Named("DefaultBooleanVariable"),
cmd.Forward(deleteVariableTask(service.booleanVariables, index)),
cmd.Reverse(restoreVariableTask(service.booleanVariables, index)))
}
func setVariableTask(variables archive.GameVariables, index int, info archive.GameVariableInfo) cmd.Task {
return func(modder world.Modder) error {
variables[index] = info
return nil
}
}
func deleteVariableTask(variables archive.GameVariables, index int) cmd.Task {
return func(modder world.Modder) error {
delete(variables, index)
return nil
}
}
func restoreVariableTask(variables archive.GameVariables, index int) cmd.Task {
info, present := variables[index]
if present {
return setVariableTask(variables, index, info)
}
return deleteVariableTask(variables, index)
}