|
|
|
@ -130,27 +130,22 @@ struct Option
|
|
|
|
|
// (and what parameters they take) |
|
|
|
|
struct Data |
|
|
|
|
{ |
|
|
|
|
// Default command name that will be used unless Acedia is configured to |
|
|
|
|
// do otherwise |
|
|
|
|
var protected Text name; |
|
|
|
|
// Short summary of what command does (recommended to |
|
|
|
|
// keep it to 80 characters) |
|
|
|
|
var protected Text summary; |
|
|
|
|
var protected array<SubCommand> subCommands; |
|
|
|
|
var protected array<Option> options; |
|
|
|
|
var protected bool requiresTarget; |
|
|
|
|
}; |
|
|
|
|
var private Data commandData; |
|
|
|
|
|
|
|
|
|
// Default command name that will be used unless Acedia is configured to |
|
|
|
|
// do otherwise |
|
|
|
|
var private const string commandName; |
|
|
|
|
|
|
|
|
|
// We do not really ever need to create more than one instance of each class |
|
|
|
|
// of `Command`, so we will simply store and reuse one created instance. |
|
|
|
|
var private Command mainInstance; |
|
|
|
|
|
|
|
|
|
var public const int TSPACE, TCOMMAND_NAME_FALLBACK, TPLUS; |
|
|
|
|
var public const int TOPEN_BRACKET, TCLOSE_BRACKET; |
|
|
|
|
var public const int TKEY, TDOUBLE_KEY, TCOMMA_SPACE, TBOOLEAN, TINDENT; |
|
|
|
|
var public const int TBOOLEAN_TRUE_FALSE, TBOOLEAN_ENABLE_DISABLE; |
|
|
|
|
var public const int TBOOLEAN_ON_OFF, TBOOLEAN_YES_NO; |
|
|
|
|
var public const int TOPTIONS, TCMD_WITH_TARGET, TCMD_WITHOUT_TARGET; |
|
|
|
|
|
|
|
|
|
protected function Constructor() |
|
|
|
|
{ |
|
|
|
|
local CommandDataBuilder dataBuilder; |
|
|
|
@ -204,21 +199,6 @@ public final static function Command GetInstance()
|
|
|
|
|
return default.mainInstance; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Returns name (in lower case) of the caller command class. |
|
|
|
|
* |
|
|
|
|
* @return Name (in lower case) of the caller command class. |
|
|
|
|
* Guaranteed to be not `none`. |
|
|
|
|
*/ |
|
|
|
|
public final static function Text GetName() |
|
|
|
|
{ |
|
|
|
|
local Text name, lowerCaseName; |
|
|
|
|
name = __().text.FromString(default.commandName); |
|
|
|
|
lowerCaseName = name.LowerCopy(); |
|
|
|
|
name.FreeSelf(); |
|
|
|
|
return lowerCaseName; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Forces command to process (parse and, if successful, execute itself) |
|
|
|
|
* player's input. |
|
|
|
@ -279,7 +259,6 @@ private final function ReportError(
|
|
|
|
|
CommandCall callInfo) |
|
|
|
|
{ |
|
|
|
|
local Text errorMessage; |
|
|
|
|
local Color previousConsoleColor; |
|
|
|
|
local ConsoleWriter console; |
|
|
|
|
if (callerPlayer == none) return; |
|
|
|
|
if (callInfo == none) return; |
|
|
|
@ -287,19 +266,18 @@ private final function ReportError(
|
|
|
|
|
|
|
|
|
|
// Setup console color |
|
|
|
|
console = callerPlayer.Console(); |
|
|
|
|
previousConsoleColor = console.GetColor(); |
|
|
|
|
if (callInfo.GetError() == CET_EmptyTargetList) { |
|
|
|
|
console.SetColor(_.color.TextWarning); |
|
|
|
|
console.UseColor(_.color.textWarning); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
console.SetColor(_.color.TextFailure); |
|
|
|
|
console.UseColor(_.color.textFailure); |
|
|
|
|
} |
|
|
|
|
// Send message |
|
|
|
|
errorMessage = callInfo.PrintErrorMessage(); |
|
|
|
|
console.Write(errorMessage); |
|
|
|
|
console.Say(errorMessage); |
|
|
|
|
errorMessage.FreeSelf(); |
|
|
|
|
// Restore console color |
|
|
|
|
console.SetColor(previousConsoleColor).Flush(); |
|
|
|
|
console.ResetColor().Flush(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Creates (and returns) empty `CommandCall` with given error type and |
|
|
|
@ -344,265 +322,37 @@ private final function Text.Character GetNewLine(Text.Formatting formatting)
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Returns colored `Text` with auto-generated help page for the caller command. |
|
|
|
|
* Returns name (in lower case) of the caller command class. |
|
|
|
|
* |
|
|
|
|
* @return Auto-generated help page for the caller `Command` class. |
|
|
|
|
* @return Name (in lower case) of the caller command class. |
|
|
|
|
* Guaranteed to be not `none`. |
|
|
|
|
*/ |
|
|
|
|
public final function Text PrintHelp() |
|
|
|
|
public final function Text GetName() |
|
|
|
|
{ |
|
|
|
|
local Text result, commandNameAsText, commandNameRandomCase; |
|
|
|
|
local MutableText builder, subBuilder; |
|
|
|
|
local Text.Formatting defaultFormatting; |
|
|
|
|
defaultFormatting = _.text.FormattingFromColor(_.color.TextDefault); |
|
|
|
|
builder = _.text.Empty(); |
|
|
|
|
// Get capitalized command name |
|
|
|
|
commandNameRandomCase = _.text.FromString(commandName); |
|
|
|
|
commandNameAsText = commandNameRandomCase.UpperCopy(); |
|
|
|
|
commandNameRandomCase.FreeSelf(); |
|
|
|
|
// Print header: name + basic info |
|
|
|
|
builder.Append(commandNameAsText, defaultFormatting); |
|
|
|
|
if (commandData.requiresTarget) { |
|
|
|
|
builder.Append(T(TCMD_WITH_TARGET), defaultFormatting); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
builder.Append(T(TCMD_WITHOUT_TARGET), defaultFormatting); |
|
|
|
|
if (commandData.name == none) { |
|
|
|
|
return P("").Copy(); |
|
|
|
|
} |
|
|
|
|
// Print commands part |
|
|
|
|
subBuilder = PrintCommands(commandNameAsText); |
|
|
|
|
builder.Append(subBuilder); |
|
|
|
|
_.memory.Free(subBuilder); |
|
|
|
|
// Print options part |
|
|
|
|
subBuilder = PrintOptions(); |
|
|
|
|
builder.Append(subBuilder); |
|
|
|
|
_.memory.Free(subBuilder); |
|
|
|
|
result = builder.Copy(); |
|
|
|
|
builder.FreeSelf(); |
|
|
|
|
return result; |
|
|
|
|
return commandData.name.LowerCopy(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private final function MutableText PrintCommands(Text commandNameAsText) |
|
|
|
|
{ |
|
|
|
|
local int i; |
|
|
|
|
local Text.Character newLine; |
|
|
|
|
local MutableText builder, subBuilder; |
|
|
|
|
local array<SubCommand> subCommands; |
|
|
|
|
newLine = GetNewLine(_.text.FormattingFromColor(_.color.TextDefault)); |
|
|
|
|
subCommands = commandData.subCommands; |
|
|
|
|
builder = _.text.Empty(); |
|
|
|
|
for (i = 0; i < subCommands.length; i += 1) |
|
|
|
|
{ |
|
|
|
|
builder.AppendCharacter(newLine); |
|
|
|
|
subBuilder = PrintSubCommand(commandNameAsText, subCommands[i]); |
|
|
|
|
builder.AppendCharacter(newLine).Append(subBuilder); |
|
|
|
|
_.memory.Free(subBuilder); |
|
|
|
|
} |
|
|
|
|
return builder; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private final function MutableText PrintOptions() |
|
|
|
|
{ |
|
|
|
|
local int i; |
|
|
|
|
local Text.Character newLine; |
|
|
|
|
local MutableText builder, subBuilder; |
|
|
|
|
local array<Option> options; |
|
|
|
|
options = commandData.options; |
|
|
|
|
if (options.length <= 0) { |
|
|
|
|
return none; |
|
|
|
|
} |
|
|
|
|
newLine = GetNewLine(_.text.FormattingFromColor(_.color.TextDefault)); |
|
|
|
|
builder = _.text.Empty(); |
|
|
|
|
builder.AppendCharacter(newLine) |
|
|
|
|
.Append(T(TOPTIONS)) |
|
|
|
|
.AppendCharacter(newLine); |
|
|
|
|
for (i = 0; i < options.length; i += 1) |
|
|
|
|
{ |
|
|
|
|
subBuilder = PrintOption(options[i]); |
|
|
|
|
builder.AppendCharacter(newLine).Append(subBuilder); |
|
|
|
|
_.memory.Free(subBuilder); |
|
|
|
|
} |
|
|
|
|
return builder; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private final function MutableText PrintSubCommand( |
|
|
|
|
Text usedCommandName, |
|
|
|
|
SubCommand subCommand) |
|
|
|
|
{ |
|
|
|
|
local MutableText builder, subBuilder; |
|
|
|
|
local Text.Formatting defaultFormatting, emphasisFormatting; |
|
|
|
|
defaultFormatting = _.text.FormattingFromColor(_.color.TextDefault); |
|
|
|
|
emphasisFormatting = _.text.FormattingFromColor(_.color.TextEmphasis); |
|
|
|
|
// Command + parameters |
|
|
|
|
builder = _.text.Empty().Append(usedCommandName, emphasisFormatting); |
|
|
|
|
if (subCommand.name != none && !subCommand.name.IsEmpty()) |
|
|
|
|
{ |
|
|
|
|
builder.Append(T(TSPACE), defaultFormatting) |
|
|
|
|
.Append(subCommand.name, emphasisFormatting); |
|
|
|
|
} |
|
|
|
|
subBuilder = PrintParameters(subCommand.required, subCommand.optional); |
|
|
|
|
builder.Append(T(TSPACE), defaultFormatting).Append(subBuilder); |
|
|
|
|
_.memory.Free(subBuilder); |
|
|
|
|
// Text description |
|
|
|
|
builder.AppendCharacter(GetNewLine(defaultFormatting)) |
|
|
|
|
.Append(T(TINDENT), defaultFormatting) |
|
|
|
|
.Append(subCommand.description, defaultFormatting); |
|
|
|
|
return builder; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private final function MutableText PrintOption(Option option) |
|
|
|
|
{ |
|
|
|
|
local Text.Character shortName; |
|
|
|
|
local MutableText builder, subBuilder; |
|
|
|
|
local Text.Formatting defaultFormatting, emphasisFormatting; |
|
|
|
|
defaultFormatting = _.text.FormattingFromColor(_.color.TextDefault); |
|
|
|
|
emphasisFormatting = _.text.FormattingFromColor(_.color.TextEmphasis); |
|
|
|
|
// Option name |
|
|
|
|
shortName = option.shortName; |
|
|
|
|
shortName.formatting = emphasisFormatting; |
|
|
|
|
builder = _.text.Empty() |
|
|
|
|
.Append(T(TKEY), emphasisFormatting) // "-" |
|
|
|
|
.AppendCharacter(shortName) |
|
|
|
|
.Append(T(TCOMMA_SPACE), defaultFormatting) //", " |
|
|
|
|
.Append(T(TDOUBLE_KEY), emphasisFormatting) //"--" |
|
|
|
|
.Append(option.longName, emphasisFormatting) |
|
|
|
|
.Append(T(TSPACE), defaultFormatting); |
|
|
|
|
// Possible options |
|
|
|
|
if (option.required.length != 0 || option.optional.length != 0) |
|
|
|
|
{ |
|
|
|
|
subBuilder = PrintParameters(option.required, option.optional); |
|
|
|
|
builder.Append(subBuilder); |
|
|
|
|
_.memory.Free(subBuilder); |
|
|
|
|
// If there actually were options - start a new line |
|
|
|
|
builder.AppendCharacter(GetNewLine(defaultFormatting)) |
|
|
|
|
.Append(T(TINDENT), defaultFormatting); |
|
|
|
|
} |
|
|
|
|
// Text description |
|
|
|
|
return builder.Append(option.description, defaultFormatting); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private final function MutableText PrintParameters( |
|
|
|
|
array<Parameter> required, |
|
|
|
|
array<Parameter> optional) |
|
|
|
|
{ |
|
|
|
|
local int i; |
|
|
|
|
local MutableText builder, subBuilder; |
|
|
|
|
local Text.Formatting defaultFormatting; |
|
|
|
|
defaultFormatting = _.text.FormattingFromColor(_.color.TextDefault); |
|
|
|
|
builder = _.text.Empty(); |
|
|
|
|
// Print required |
|
|
|
|
for (i = 0; i < required.length; i += 1) |
|
|
|
|
{ |
|
|
|
|
subBuilder = PrintParameter(required[i]); |
|
|
|
|
builder.Append(subBuilder); |
|
|
|
|
_.memory.Free(subBuilder); |
|
|
|
|
if (i < required.length - 1) { |
|
|
|
|
builder.Append(T(TSPACE), defaultFormatting); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (optional.length <= 0) { |
|
|
|
|
return builder; |
|
|
|
|
} |
|
|
|
|
// Print optional |
|
|
|
|
builder.Append(T(TSPACE), defaultFormatting) |
|
|
|
|
.Append(T(TOPEN_BRACKET), defaultFormatting); |
|
|
|
|
for (i = 0; i < optional.length; i += 1) |
|
|
|
|
{ |
|
|
|
|
subBuilder = PrintParameter(optional[i]); |
|
|
|
|
builder.Append(subBuilder); |
|
|
|
|
_.memory.Free(subBuilder); |
|
|
|
|
if (i < optional.length - 1) { |
|
|
|
|
builder.Append(T(TSPACE), defaultFormatting); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
builder.Append(T(TCLOSE_BRACKET), defaultFormatting); |
|
|
|
|
return builder; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private final function MutableText PrintParameter(Parameter parameter) |
|
|
|
|
{ |
|
|
|
|
local MutableText builder; |
|
|
|
|
local Text.Formatting defaultFormatting, typeFormatting; |
|
|
|
|
defaultFormatting = _.text.FormattingFromColor(_.color.TextDefault); |
|
|
|
|
switch (parameter.type) |
|
|
|
|
{ |
|
|
|
|
case CPT_Boolean: |
|
|
|
|
typeFormatting = _.text.FormattingFromColor(_.color.TypeBoolean); |
|
|
|
|
break; |
|
|
|
|
case CPT_Integer: |
|
|
|
|
typeFormatting = _.text.FormattingFromColor(_.color.TypeNumber); |
|
|
|
|
break; |
|
|
|
|
case CPT_Number: |
|
|
|
|
typeFormatting = _.text.FormattingFromColor(_.color.TypeNumber); |
|
|
|
|
break; |
|
|
|
|
case CPT_Text: |
|
|
|
|
typeFormatting = _.text.FormattingFromColor(_.color.TypeString); |
|
|
|
|
break; |
|
|
|
|
case CPT_Object: |
|
|
|
|
typeFormatting = _.text.FormattingFromColor(_.color.TypeLiteral); |
|
|
|
|
break; |
|
|
|
|
case CPT_Array: |
|
|
|
|
typeFormatting = _.text.FormattingFromColor(_.color.TypeLiteral); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
} |
|
|
|
|
builder = _.text.Empty().Append(parameter.displayName, typeFormatting); |
|
|
|
|
if (parameter.allowsList) { |
|
|
|
|
builder.Append(T(TPLUS), typeFormatting); |
|
|
|
|
} |
|
|
|
|
return builder; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private final function Text PrintBooleanType(PreferredBooleanFormat booleanType) |
|
|
|
|
/** |
|
|
|
|
* Returns `Command.Data` struct that describes caller `Command`. |
|
|
|
|
* |
|
|
|
|
* @return `Command.Data` that describes caller `Command`. Returned struct |
|
|
|
|
* contains `Text` references that are used internally by the `Command` |
|
|
|
|
* and not their copies. |
|
|
|
|
* Generally this is undesired approach and leaves `Command` more |
|
|
|
|
* vulnerable to modification, but copying all the data inside would not |
|
|
|
|
* only introduce a largely pointless computational overhead, but also |
|
|
|
|
* would require some cumbersome logic. This might change in the future, |
|
|
|
|
* so deallocating any objects in the returned `struct` would lead to |
|
|
|
|
* undefined behavior. |
|
|
|
|
*/ |
|
|
|
|
public final function Data GetData() |
|
|
|
|
{ |
|
|
|
|
switch (booleanType) |
|
|
|
|
{ |
|
|
|
|
case PBF_TrueFalse: |
|
|
|
|
return T(TBOOLEAN_TRUE_FALSE); |
|
|
|
|
case PBF_EnableDisable: |
|
|
|
|
return T(TBOOLEAN_ENABLE_DISABLE); |
|
|
|
|
case PBF_OnOff: |
|
|
|
|
return T(TBOOLEAN_ON_OFF); |
|
|
|
|
case PBF_YesNo: |
|
|
|
|
return T(TBOOLEAN_YES_NO); |
|
|
|
|
default: |
|
|
|
|
} |
|
|
|
|
return T(TBOOLEAN); |
|
|
|
|
return commandData; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
defaultproperties |
|
|
|
|
{ |
|
|
|
|
TSPACE = 0 |
|
|
|
|
stringConstants(0) = " " |
|
|
|
|
TPLUS = 1 |
|
|
|
|
stringConstants(1) = "(+)" |
|
|
|
|
TOPEN_BRACKET = 2 |
|
|
|
|
stringConstants(2) = "[" |
|
|
|
|
TCLOSE_BRACKET = 3 |
|
|
|
|
stringConstants(3) = "]" |
|
|
|
|
TKEY = 4 |
|
|
|
|
stringConstants(4) = "-" |
|
|
|
|
TDOUBLE_KEY = 5 |
|
|
|
|
stringConstants(5) = "--" |
|
|
|
|
TCOMMA_SPACE = 6 |
|
|
|
|
stringConstants(6) = ", " |
|
|
|
|
TINDENT = 7 |
|
|
|
|
stringConstants(7) = " " |
|
|
|
|
TBOOLEAN = 8 |
|
|
|
|
stringConstants(8) = "boolean" |
|
|
|
|
TBOOLEAN_TRUE_FALSE = 9 |
|
|
|
|
stringConstants(9) = "true/false" |
|
|
|
|
TBOOLEAN_ENABLE_DISABLE = 10 |
|
|
|
|
stringConstants(10) = "enable/disable" |
|
|
|
|
TBOOLEAN_ON_OFF = 11 |
|
|
|
|
stringConstants(11) = "on/off" |
|
|
|
|
TBOOLEAN_YES_NO = 12 |
|
|
|
|
stringConstants(12) = "yes/no" |
|
|
|
|
TCMD_WITH_TARGET = 13 |
|
|
|
|
stringConstants(13) = ": This command requires target to be specified." |
|
|
|
|
TCMD_WITHOUT_TARGET = 14 |
|
|
|
|
stringConstants(14) = ": This command does not require target to be specified." |
|
|
|
|
TOPTIONS = 15 |
|
|
|
|
stringConstants(15) = "OPTIONS" |
|
|
|
|
// Under normal conditions we only create one instance of each, so |
|
|
|
|
// there is no need to object pools |
|
|
|
|
usesObjectPool = false |
|
|
|
|
} |