Repository navigation
EN UO Topic files json data
Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
Home · C# · IDE · UO API · A–Z · Examples · Languages
Files, JSON, arrays and collections C#, and compatible helpers UO. Built-in functions C# are written without UO.
| Topic | Commands and explanations |
|---|---|
| File | C# and standard library |
| JSON Parse | C# and standard library |
| JSON Load | C# and standard library)) |
| JSON Save | C# and standard library |
| JSON Stringify | C# and standard library |
| List | C# and standard library |
| Dictionary | C# and standard library |
| Arrays | C# and standard library · C# and standard library |
| MemoryStream | C# and standard library |
UO.GetArrayLength · UO.GetWord · UO.GetWordCount
C# and standard library · C# and standard library · C# and standard library
Each command card has several independent scenarios: direct code, functions and classes. Below are selected short scenarios; restrictions and full analysis are in the cards.
Parameters, result and all scenarios
dynamic[] amounts = new dynamic[checked((int)(2) + 1)];
amounts[0] = 50;
amounts[1] = 75;
amounts[2] = 25;
var total = 0;
for (int index = (int)(0); index <= UO.GetArrayLength(amounts) - 1; index += (int)(1))
{
total += amounts[index];
}
return total;Parameters, result and all scenarios
return ReadX("move 1445 1690");
dynamic ReadX(dynamic text)
{
if (Truth(UO.GetWordCount(text) != 3))
{
return -1;
}
if (Truth(UO.GetWord(text, 0) != "move"))
{
return -1;
}
var xText = UO.GetWord(text, 1);
if (!(Truth(IsNumeric(xText))))
{
return -1;
}
var x = Convert.ToDouble(xText);
if (Truth(Convert.ToString(x) == "NaN") || Truth(x < 0) || Truth(x > 65535))
{
return -1;
}
if (Truth(x != Math.Truncate(x)))
{
return -1;
}
return Convert.ToInt32(x);
}
bool IsNumeric(object? value) => double.TryParse(Convert.ToString(value), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out _);
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
dynamic UO.GetArrayLength(object? arg);Counts immediate array cells for limited iteration and safe index reading.
int >= 0. An empty Array and a value of another kind give 0: the result alone cannot check the type. This is a quantity, not a bool and not the last index.
- Local check: does not send a request to the server, does not move items or modify the array. The examples contain training data, not live inventory.
- Checks the type of value and reads Array.Length without traversing or summing. declaring the variable values(2) creates three cells, indexes 0..2, including empty ones.
- Nested elements are not counted recursively. For a string there is Len, for List/Dictionary there is their Count. UO.GetArrayLength is a compatible name for the same local check.
Each example can be saved as a separate file .csx and run in IDE. The UO object and wait functions provide the host of the current game session. Replace demo serial names, coordinates and addresses with your own.
Run this code as a separate .csx file. Arguments appear directly in the call; replace demonstration names, serials and addresses with your own.
The result of one call is printed in the journal. A number is not converted to bool; an array is displayed as its element count.
object? result = UO.GetArrayLength(new[] { 10, 20, 30 });
UO.Print(result is Array values ? "Count: " + values.Length : Convert.ToString(result));Parameters and execution flow:
- declaring the variable amounts(2) creates three entries. The loop iterates over the indices 0..2 and returns 150. The quantities are added by the cycle, not by ArrayLength.
- Local check: does not send a request to the server, does not move items or modify the array. The examples contain training data, not live inventory.
dynamic[] amounts = new dynamic[checked((int)(2) + 1)];
amounts[0] = 50;
amounts[1] = 75;
amounts[2] = 25;
var total = 0;
for (int index = (int)(0); index <= UO.GetArrayLength(amounts) - 1; index += (int)(1))
{
total += amounts[index];
}
return total;Parameters and execution flow:
- There are two Array points in route, each with three coordinate cells. The result is 2|3, not six. The character does not move.
- Local check: does not send a request to the server, does not move items or modify the array. The examples contain training data, not live inventory.
return RunScenario();
object? RunScenario()
{
dynamic[] first = new dynamic[checked((int)(2) + 1)];
first[0] = 100;
first[1] = 200;
first[2] = 0;
dynamic[] second = new dynamic[checked((int)(2) + 1)];
second[0] = 101;
second[1] = 200;
second[2] = 0;
dynamic[] route = new dynamic[checked((int)(1) + 1)];
route[0] = first;
route[1] = second;
return Convert.ToString(UO.GetArrayLength(route)) + "|" + Convert.ToString(UO.GetArrayLength(route[0]));
return null;
}Parameters and execution flow:
- The line gives 0; Main returns no route before accessing the index. The check is also suitable for an empty Array, but does not distinguish between these cases.
- Local check: does not send a request to the server, does not move items or modify the array. The examples contain training data, not live inventory.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
private readonly Func<int, Task> WaitAsync;
private readonly string[] Args;
private readonly CancellationToken Cancellation;
private readonly Action Checkpoint;
public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
{
UO = api;
WaitAsync = wait;
Args = args;
Cancellation = cancellation;
Checkpoint = checkpoint;
}
public object? RunScenario()
{
var route = "not an array";
if (Truth(UO.GetArrayLength(route) == 0))
{
return "no route";
}
return route[0];
return null;
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: arg (Any)
Result: dynamic / Any.
During a call:
Internal implementation code; this is not a standalone script.
public static InjectionValue GetArrayLength(InjectionValue arg) => arg.Kind==InjectionValueKind.Array?new InjectionValue(arg.Array.Length):InjectionValue.Zero;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
string UO.GetWord(object? value, object? wordIndex);
string UO.GetWord(object? value, object? wordIndex, object? delimiter);Gets a single word or field from text. Useful for parsing coordinates, a list of priorities, or saved script settings.
-
value— Source string. The function parses exactly the transmitted text: it does not read the journal, file, equipment or world on its own. -
wordIndex— Whole index of the word from 0: 0 is the first, 1 is the second. A negative index and an index not less than the number of words return an empty string. -
delimiter— Optional delimiter. Without it, with "" or with a single space " ", the text is divided by any Unicode whitespace characters, including tabs and newlines. Another delimiter is the exact sequence in its entirety: "::" means two colons in a row, not every colon. Empty parts are discarded; non-empty parts are not trimmed.
string — the selected non-empty part, or "" if there is no index. This is text, not ID, type or number: for number, check IsNumeric separately and convert the value.
- Works locally, without packages or game actions. GetWord and UO.GetWord call the same handler; GetWordCount and UO.GetWordCount are also the same. Each call re-splits the entire text. In the loop, save the quantity in advance; For large repeated parsing, it is better to store already parsed fields in an array. This is not a CSV parser: quotes do not protect delimiters, empty columns are lost.
- GetWord · GetWordCount · IsNumeric
Each example can be saved as a separate file .csx and run in IDE. The UO object and wait functions provide the host of the current game session. Replace demo serial names, coordinates and addresses with your own.
Run this code as a separate .csx file. Arguments appear directly in the call; replace demonstration names, serials and addresses with your own.
The result of one call is printed in the journal. A number is not converted to bool; an array is displayed as its element count.
var result = UO.GetWord("red green blue", 1);
UO.Print("GetWord: " + Convert.ToString(result));Parameters and execution flow:
- ReadX receives the string "move 1445 1690". Tests three words and the name move; index 1 selects X. IsNumeric checks the text, Convert. ToInt32 returns 1445. This is just a breakdown of the setup; the character doesn't move. If the form is incorrect, -1 is returned.
- ReadX also requires the whole X in the range 0..65535 before Convert.ToInt32; NaN, infinity and fractional X are rejected. Y is not tested here.
return ReadX("move 1445 1690");
dynamic ReadX(dynamic text)
{
if (Truth(UO.GetWordCount(text) != 3))
{
return -1;
}
if (Truth(UO.GetWord(text, 0) != "move"))
{
return -1;
}
var xText = UO.GetWord(text, 1);
if (!(Truth(IsNumeric(xText))))
{
return -1;
}
var x = Convert.ToDouble(xText);
if (Truth(Convert.ToString(x) == "NaN") || Truth(x < 0) || Truth(x > 65535))
{
return -1;
}
if (Truth(x != Math.Truncate(x)))
{
return -1;
}
return Convert.ToInt32(x);
}
bool IsNumeric(object? value) => double.TryParse(Convert.ToString(value), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out _);
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;Parameters and execution flow:
- PriorityAt gets the list "mage||tank|archer|" and index=1. Separator | discards empty elements, so the result is tank. Names are arbitrary; here they do not register ClassMask or check clothing. Use the same division rules when counting and selecting.
return RunScenario();
object? RunScenario()
{
return PriorityAt("mage||tank|archer|", 1);
return null;
}
dynamic PriorityAt(dynamic names, dynamic index)
{
return UO.GetWord(names, index, "|");
}Parameters and execution flow:
- ReadPlace divides "Britain::Bank:west" with the exact delimiter ::. The index 1 returns Bank:west, leaving the inner single colon. If there are not exactly two non-empty parts, the helper returns "". Spaces around parts are preserved.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
private readonly Func<int, Task> WaitAsync;
private readonly string[] Args;
private readonly CancellationToken Cancellation;
private readonly Action Checkpoint;
public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
{
UO = api;
WaitAsync = wait;
Args = args;
Cancellation = cancellation;
Checkpoint = checkpoint;
}
public object? RunScenario()
{
return ReadPlace("Britain::Bank:west");
return null;
}
dynamic ReadPlace(dynamic text)
{
if (Truth(UO.GetWordCount(text, "::") != 2))
{
return "";
}
return UO.GetWord(text, 1, "::");
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: value (String), wordIndex (Integer)
Result: string / String.
During a call:
-
InjectionScript.Runtime.InjectionApi.GetWord. -
InjectionScript.Runtime.InjectionApi.GetWord:GetWord(value,wordIndex," ").
Internal implementation code; this is not a standalone script.
private static string GetWord(string value,int wordIndex) => GetWord(value,wordIndex," ");Parameters: value (String), wordIndex (Integer), delimiter (String)
Result: string / String.
During a call:
-
InjectionScript.Runtime.InjectionApi.GetWord. -
InjectionScript.Runtime.InjectionApi.SplitWords:SplitWords(value,delimiter).
Internal implementation code; this is not a standalone script.
private static string GetWord(string value,int wordIndex,string delimiter)
{
var words=SplitWords(value,delimiter);return wordIndex>=0&&wordIndex<words.Length?words[wordIndex]:string.Empty;
}Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
int UO.GetWordCount(object? value);
int UO.GetWordCount(object? value, object? delimiter);Counts non-empty text fields. Allows you to check the number of coordinates before parsing and determine the boundaries of enumeration of the list.
-
value— Source string. The function parses exactly the transmitted text: it does not read the journal, file, equipment or world on its own. -
delimiter— Optional delimiter. Without it, with "" or with a single space " ", the text is divided by any Unicode whitespace characters, including tabs and newlines. Another delimiter is the exact sequence in its entirety: "::" means two colons in a row, not every colon. Empty parts are discarded; non-empty parts are not trimmed.
int — number of non-empty parts. An empty string and a delimited-only string yield 0. This is a counter, not a bool: 2 is also a valid result; check count > 0 or the exact quantity you need.
- Works locally, without packages or game actions. GetWord and UO.GetWord call the same handler; GetWordCount and UO.GetWordCount are also the same. Each call re-splits the entire text. In the loop, save the quantity in advance; For large repeated parsing, it is better to store already parsed fields in an array. This is not a CSV parser: quotes do not protect delimiters, empty columns are lost.
- GetWord · GetWordCount · IsNumeric
Each example can be saved as a separate file .csx and run in IDE. The UO object and wait functions provide the host of the current game session. Replace demo serial names, coordinates and addresses with your own.
Run this code as a separate .csx file. Arguments appear directly in the call; replace demonstration names, serials and addresses with your own.
The result of one call is printed in the journal. A number is not converted to bool; an array is displayed as its element count.
var result = UO.GetWordCount("red green blue");
UO.Print("GetWordCount: " + Convert.ToString(result));Parameters and execution flow:
- HasPoint accepts "1445 1690 0". Requires exactly three words and numeric text in each, then returns 1; otherwise 0. This is only a check of the form, and not the validity of the map, coordinate boundaries or passability. GetWordCount here returns 3 and bool is returned by our helper.
- HasPoint additionally rejects NaN and infinities, which IsNumeric itself can accept. Fractional finite numbers are allowed; Before movement, a separate check of entire game coordinates is required.
return HasPoint("1445 1690 0");
dynamic HasPoint(dynamic text)
{
if (Truth(UO.GetWordCount(text) != 3))
{
return 0;
}
var i = 0;
while (Truth(i < 3))
{
if (!(Truth(IsNumeric(UO.GetWord(text, i)))))
{
return 0;
}
var value = Convert.ToString(Convert.ToDouble(UO.GetWord(text, i)));
if (Truth(value == "NaN") || Truth(value == "Infinity") || Truth(value == "-Infinity"))
{
return 0;
}
i = i + 1;
}
return 1;
}
bool IsNumeric(object? value) => double.TryParse(Convert.ToString(value), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out _);
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;Parameters and execution flow:
- JoinPriorities gets "mage||tank|archer|". Counts three non-empty elements once, iterates through the indices 0..2 and returns "mage > tank > archer". Beginning, ending and repeating | do not create a separate priority.
return RunScenario();
object? RunScenario()
{
return JoinPriorities("mage||tank|archer|");
return null;
}
dynamic JoinPriorities(dynamic names)
{
var count = UO.GetWordCount(names, "|");
var i = 0;
var result = "";
while (Truth(i < count))
{
if (Truth(i > 0))
{
result = result + " > ";
}
result = result + UO.GetWord(names, i, "|");
i = i + 1;
}
return result;
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;Parameters and execution flow:
- CountPlaces gets "Britain::Bank:west::". The :: separator gives two fields, the trailing empty field is removed: the result is 2. Single: Bank:west does not split text within. This is not a list of cities from the client, but an example of your setup format.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
private readonly Func<int, Task> WaitAsync;
private readonly string[] Args;
private readonly CancellationToken Cancellation;
private readonly Action Checkpoint;
public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
{
UO = api;
WaitAsync = wait;
Args = args;
Cancellation = cancellation;
Checkpoint = checkpoint;
}
public object? RunScenario()
{
return CountPlaces("Britain::Bank:west::");
return null;
}
dynamic CountPlaces(dynamic text)
{
return UO.GetWordCount(text, "::");
}
}C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: value (String)
Result: int / Integer.
During a call:
-
InjectionScript.Runtime.InjectionApi.GetWordCount. -
InjectionScript.Runtime.InjectionApi.GetWordCount:GetWordCount(value," ").
Internal implementation code; this is not a standalone script.
private static int GetWordCount(string value) => GetWordCount(value," ");Parameters: value (String), delimiter (String)
Result: int / Integer.
During a call:
-
InjectionScript.Runtime.InjectionApi.GetWordCount. -
InjectionScript.Runtime.InjectionApi.SplitWords:SplitWords(value,delimiter).
Internal implementation code; this is not a standalone script.
private static int GetWordCount(string value,string delimiter) => SplitWords(value,delimiter).Length;Українська · Русский · English · Deutsch · Français · Italiano · Español · 繁體中文 · 日本語 · 한국어
int UO.Random(object? max);
int UO.Random(object? min, object? max);Returns a random integer. The new form UO.Random(min, max) selects the number FROM min TO max inclusive. The previous form UO.Random(max) is retained: it selects from 0 to max−1. The result can be stored in a variable and used as a delay, coordinate, index or condition.
-
min— The first argument of the two-argument form ONLY is the lower bound included, an integer from −2147483648 to 2147483647. Can be negative, zero or positive. Should not be more than max. In the one-argument form there is no min, and the passed number means max. -
max— In two-argument form, the second argument, the included upper bound int from −2147483648 to 2147483647. In the former one-argument form, a single argument, EXCLUDED upper bound, an integer from 0 to 2147483647; Random(0) returns 0. A negative max in one-argument form is not allowed.
int is one pseudo-random integer. With two arguments min ≤ result ≤ max; both borders can fall out. With one positive argument 0 ≤ result < max. Repeating a previously drawn number is acceptable: the team does not select numbers without repetitions.
- Exactly two forms are supported: UO.Random(max) and UO.Random(min, max). A call without arguments is not registered. For a "from and to" range, use two arguments.
- Random(5, 5) always returns 5. Random(10, 1) reports a range error: boundaries are not silently swapped. Random(-10, -1) is correct because −10 is less than −1.
- Both boundaries are integers int; for decimal fractions, you can select whole units of the desired scale and then divide. For example, Random(0, 100) / 100.0 gives discrete values from 0 to 1 in increments of 0,01.
- The full range of Random(-2147483648, 2147483647) is acceptable. The implementation does not add 1 to max in int and therefore does not overflow at the upper bound. For wide ranges, discarding extra values is used so that the remainder of the division does not bias the selection.
- The call calculates the number locally, does not send packets, and does not itself pause. For a random pause, pass the result to UO.Wait. Your script should maintain the minimum latency required by the server.
- Store the result in var if you need the same number multiple times. Two separate calls to Random may return different values. The command does not check whether the selected square is valid for movement or transfer: this is checked by the corresponding game command.
Each example can be saved as a separate file .csx and run in IDE. The UO object and wait functions provide the host of the current game session. Replace demo serial names, coordinates and addresses with your own.
Run this code as a separate .csx file. Arguments appear directly in the call; replace demonstration names, serials and addresses with your own.
The result of one call is printed in the journal. A number is not converted to bool; an array is displayed as its element count.
var result = UO.Random(10);
UO.Print("Random: " + Convert.ToString(result));Parameters and execution flow:
- min gets lower=1, max gets upper=6. There are exactly six possible values: 1, 2, 3, 4, 5 and 6.
- roll stores one received number. Print displays exactly this; Convert.ToString converts int to a string. Running it again may give the same number.
var lower = 1;
var upper = 6;
var roll = UO.Random(lower, upper);
UO.Print("Roll: " + Convert.ToString(roll));Parameters and execution flow:
- min=350 and max=700 define the enabled delay limits. The result, such as 350, 512 or 700, is written to delay.
- UO.Wait receives one delay argument in milliseconds. The pause occurs in UO.Wait; Random only calculates the number.
- Choose the lower limit based on the actions and requirements of your server. The random delay is not a substitute for waiting for the server to respond if the next action needs that response.
return RunScenario();
object? RunScenario()
{
var delay = UO.Random(350, 700);
UO.Print("Waiting " + Convert.ToString(delay) + " ms");
UO.Wait(delay);
UO.Print("Wait finished");
return null;
}Parameters and execution flow:
- min=1, max=3: choice is 1, 2 or 3. Each branch corresponds to one of the valid numbers.
- The call to Random is executed once before the if. If you call it again in each condition, these will be different elections.
- Replace Print with the desired actions in your script, keeping the condition, range, and server response checks for those actions.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
private readonly Func<int, Task> WaitAsync;
private readonly string[] Args;
private readonly CancellationToken Cancellation;
private readonly Action Checkpoint;
public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
{
UO = api;
WaitAsync = wait;
Args = args;
Cancellation = cancellation;
Checkpoint = checkpoint;
}
public object? RunScenario()
{
var choice = UO.Random(1, 3);
if (Truth(choice == 1))
{
UO.Print("First action");
}
else
{
if (Truth(choice == 2))
{
UO.Print("Second action");
}
else
{
UO.Print("Third action");
}
}
return null;
}
bool Truth(object? value) => value is bool flag ? flag : value is string text ? !string.IsNullOrEmpty(text) : value != null && Convert.ToDouble(value) != 0;
}Parameters and execution flow:
- First Random: min=50, max=90, result x. Second: min=60, max=100, result y. These are two independent coordinates inside the container, not screen coordinates or map coordinates.
- In MassMove delay=350 ms, quantity=10 integer items, type=0 is determined by the first manual transfer, destination=backpack, x/y are taken from variables, z=0.
- After the command, drag the sample with the mouse from the original container. This entire inclusion of MassMove uses one selected position. Randomizing the position of each individual item requires a loop with separate MoveItem rather than a single call to MassMove.
- Select the X/Y range within the available area of your backpack. The server may change the placement of items.
var x = UO.Random(50, 90);
var y = UO.Random(60, 100);
UO.MassMove(350, 10, 0, "backpack", x, y, 0);
UO.Print("Drag a source item; position: " + Convert.ToString(x) + ", " + Convert.ToString(y));Parameters and execution flow:
- In both calls min=−2 and max=2: −2, −1, 0, 1, 2 are possible. GetX/GetY reads the character's coordinates without arguments.
- x/y is just a calculated cell in a square around the character. This example does not start moving or throw objects.
- A random cell may be impassable or beyond the border of the map. Before game action, use appropriate coordinate and route checks.
return RunScenario();
object? RunScenario()
{
var offsetX = UO.Random(-2, 2);
var offsetY = UO.Random(-2, 2);
var x = UO.GetX() + offsetX;
var y = UO.GetY() + offsetY;
UO.Print("Candidate tile: " + Convert.ToString(x) + ", " + Convert.ToString(y));
return null;
}Parameters and execution flow:
- In Random(count) there is only max=10: no upper bound, 0..9 possible. The number 10 does not appear.
- In Random(0, count−1) min=0 and max=9 inclusive: the valid range is the same, but the call makes a new independent choice.
- count must be positive for index selection. The special compatible case Random(0)=0 does not mean that the empty collection has an element at index 0.
return new ScriptScenario(UO, WaitAsync, Args, Cancellation, Checkpoint).RunScenario();
class ScriptScenario
{
private readonly global::ClassicUO.CSharp.CSharpGameApi UO;
private readonly Func<int, Task> WaitAsync;
private readonly string[] Args;
private readonly CancellationToken Cancellation;
private readonly Action Checkpoint;
public ScriptScenario(global::ClassicUO.CSharp.CSharpGameApi api, Func<int, Task> wait, string[] args, CancellationToken cancellation, Action checkpoint)
{
UO = api;
WaitAsync = wait;
Args = args;
Cancellation = cancellation;
Checkpoint = checkpoint;
}
public object? RunScenario()
{
var count = 10;
var index = UO.Random(count);
var equivalent = UO.Random(0, count - 1);
UO.Print("Index 0..9: " + Convert.ToString(index));
UO.Print("Another independent index: " + Convert.ToString(equivalent));
return null;
}
}Parameters and execution flow:
- min=7 and max=7 give a single result 7.
- min=−100 and max=−50 give only negative numbers, including both boundaries.
- The last call covers the entire signed 32-bit int. Its result can be positive, negative or zero.
- Boundaries are passed from smaller to larger. The erroneous call to Random(10, 1) stops with the message min must not exceed max; it does not need to be used for an inverted range.
var fixedValue = UO.Random(7, 7);
var negative = UO.Random(-100, -50);
var signedValue = UO.Random(-2147483648, 2147483647);
UO.Print("Fixed: " + Convert.ToString(fixedValue));
UO.Print("Negative: " + Convert.ToString(negative));
UO.Print("Signed Integer: " + Convert.ToString(signedValue));C# is case sensitive. Use the exact UO name. Compare numeric flags with 0; use bool only where the signature returns bool.
Parameters: max (Integer)
Result: int / Integer.
During a call:
Internal implementation code; this is not a standalone script.
public int Random(int max)
{
lock (randomSync) return random.Next(max);
}Parameters: min (Integer), max (Integer)
Result: int / Integer.
During a call:
Internal implementation code; this is not a standalone script.
public int Random(int min, int max)
{
if (min > max)
throw new NativeSubrutineException("UO.Random(min, max): min must not exceed max.");
if (min == max) return min;
long count = (long)max - min + 1;
lock (randomSync)
{
if (count <= int.MaxValue)
return (int)(min + (long)random.Next((int)count));
// Inclusive Int32.MaxValue and the full signed range must not
// overflow. Rejection avoids modulo bias; reuse the four bytes.
const ulong domain = 1UL << 32;
ulong limit = domain - domain % (ulong)count;
uint sample;
do
{
random.NextBytes(randomRangeBytes);
sample = BitConverter.ToUInt32(randomRangeBytes, 0);
} while (sample >= limit);
return (int)(min + (long)(sample % (ulong)count));
}
}ClassicUO — Bad Newbie & C# Scripts IDE