http://wiki.offgridthegame.com/api.php?action=feedcontributions&user=WikiAdmin&feedformat=atomOff Grid Wiki - User contributions [en]2024-03-28T08:11:25ZUser contributionsMediaWiki 1.41.0http://wiki.offgridthegame.com/index.php?title=Game_Progress&diff=2036Game Progress2024-02-14T17:40:52Z<p>WikiAdmin: /* GameProgress */</p>
<hr />
<div>Off Grid is built to support much more complicated stories than the linear main story is. And it's also built to allow modders to hook their own new missions or stories in middle of the original story and it's missions. This means that there is no existing script that tells"when you've completed this level, load this other level next" and so on until you've completed the game. Instead, we've set up a system based on simple preconditions and effects that tell what's needed for a mission to "unlock" in game so it can be played, and how completing that mission changes the game world (or story, or characters, or whatever you might want).<br />
<br />
== Missions ==<br />
<br />
Each mission has a '''''level.json''''' file that tells the game some necessary background data about the mission. Which asset bundles are needed, which Unity scene to load, where and when the mission takes place and so on. If you are using LevelKit, you don't need to edit this file by hand, the LevelKit tools let you fill in all the required information. But which ever way you do it, you'll probably want to make sure you fill in suitable GameProgress information to control when your mission should be playable, and how it affects everything else in the game (if it does, of course).<br />
<br />
<br />
== GameProgress ==<br />
<br />
The GameProgress is a list of key & value pairs, stored as part of your save games, and the main mechanism for telling what missions you've completed, which ones are available now, and also what specific extra objectives you might have completed, the player's relationships with different characters due to choices made in conversations and so on. The list itself is simple enough, each piece of saved data has a name, and a value (which is just a text string).<br />
<br />
For example your save game might contain data like this:<br />
* "ApartmentIntroCompleted" = "false"<br />
* "ApartmentIntro_SelectedHintLevel" = "Extra"<br />
* "IntroductionCompleted" = "true"<br />
* "NewspaperCompleted" = "false"<br />
* "FoundDevLaptopInHarbor" = "true"<br />
* "CourthouseCompleted" = "false"<br />
* "Stats_HackedDevices" = 72<br />
<br />
It's worth keeping in mind that the values are stored as strings, so ''"true"'' and ''"True"'' would not be the same thing, and you are not limited to saving just true/false values, for example if you want to do a branching story you can save the name of the branch in the game progress and easily trigger different things based on that information.<br />
<br />
Since this information can be used (and changed) throughout the game, in your mission scripts, conversations, based on devices you hack and files you gain, it's worth using names that actually describe something that was done. That way it's much easier to keep track of what the data actually means, and to hook into the same values in more interesting ways, and to combine and mix different levels and mods and stories together.<br />
<br />
For example, if the objective of your mission is to connect a server at Semaeopus Headquarter's data center to the Mesh network, you might want the effect of completing that mission to be ''"SemaeopusServerConnectedToMESH" = "true"'' rather than ''"CompletedLevel12" = "true"''. Think of it as events happening in your story rather than just mechanism for triggering the next mission.<br />
<br />
=== Preconditions ===<br />
<br />
Preconditions are used to tell when the mission is available to play. Each value listed here ''must'' exist in the saved GameProgress, and the value ''must'' match with the precondition.<br />
<br />
If any listed value doesn't match, or is missing, that mission will not be selectable to play in the map screen until the conditions are met.<br />
<br />
=== Effects ===<br />
<br />
Effects describe what changes when player completes the mission. Usually this would be what you want to happen in overall GameProgress as the result of that mission being completed, and what's needed for the next mission to become playable. However it's not ''required'' to have an effect, for example if you just want a mission that can be played again and again for some reason. Or, if you are setting the required values from something else in your mission. (for clarity, it's probably best idea to set it here if there's only one possible outcome for the mission anyway).<br />
<br />
If you are building a branching narrative, you could leave the mission effect empty, and instead set some value in your mission script (or some other script, of course) based on the choices the player made. As far as the GameProgress system is concerned, there's no difference between a value set as part of mission definition versus a value set somewhere else.<br />
<br />
=== Rules ===<br />
<br />
Rules allow you to set some initial values in GameProgress. Any value that doesn't exist in the GameProgress yet will be added there as soon as the game recognizes a mission with a rule. So in practice the first time you start a new game, it looks for all rules form all missions in the game, and fills in your GameProgress based on them. And if you later on get a mod or expansion that adds new missions, the first time you start the game with those installed their rules will be added as well (as long as they don't try to overwrite some value that was already there, of course).<br />
<br />
For most of the mission you create you shouldn't need to add any rules at all. But when you create a new storyline, you might need something in place to make the first mission of the story playable or to add some other background data about that story.<br />
<br />
=== Example of multiple missions ===<br />
<br />
=== About level.json syntax ===<br />
If you for some reason end needing to edit the level.json file by hand, you'll notice the keys and values are stored on separate arrays. This is for sake of easy reading & writing of those files from the game. Just put the keys and values in their own arrays in matching order.<br />
<br />
<syntaxhighlight source lang="json" line><br />
"preconditions": {<br />
"keyArray": [<br />
"NewspaperCompleted",<br />
"WorkplaceCompleted"<br />
],<br />
"valueArray": [<br />
"false",<br />
"true"<br />
]<br />
},<br />
"effects": {<br />
"keyArray": [<br />
"NewspaperCompleted"<br />
],<br />
"valueArray": [<br />
"true"<br />
]<br />
},<br />
"rules": {<br />
"keyArray": [<br />
"NewspaperCompleted"<br />
],<br />
"valueArray": [<br />
"false"<br />
]<br />
}<br />
</syntaxhighlight><br />
<br />
== Using GameProgress in mission scripts, conversations etc ==<br />
You can check if a value exists in GameProgress, read the value, change it or add new one from any Lua script in Off Grid. This is all done using the [[GameProgress Lua API]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Agent_Definitions&diff=1937Agent Definitions2024-02-01T14:46:55Z<p>WikiAdmin: /* Actions */</p>
<hr />
<div>Each AI's behaviour is defined by its Agent definition.<br />
<br />
= Concepts =<br />
<br />
== GOAP State ==<br />
The World State and Goal states are made up of GOAP States. GOAP stands for "Goal-Oriented Action Planning". Each state comprises a unique string ID, and a boolean (true/false) value. The state as a whole is made up of multiple state items.<br />
{| class="wikitable"<br />
!colspan="2" | GOAP State Item<br />
|-<br />
! Name !! Description<br />
|-<br />
| ''state'' || The unique name of the state.<br />
|-<br />
| ''value'' || The true/false value.<br />
|}<br />
A state is made up of 1-n items. This can be represented either as<br />
<syntaxhighlight source lang="lua" line start=5><br />
{<br />
{ state = "amused", value = false },<br />
{ state = "tired", value = false },<br />
}<br />
</syntaxhighlight><br />
or, for a state with a single item in, as<br />
<syntaxhighlight source lang="lua" line start=5><br />
{ state = "amused", value = false }<br />
</syntaxhighlight><br />
...which saves some slightly untidy extra braces. Note that in the former example, the items are just an anonymous list, the keys are implicit. GOAP State is a type that will appear throughout this guide and it is always parsed in the same way.<br />
<br />
== Personality ==<br />
Each AI (TBC: Human AI?) should have a [[Character Profiles#Character personality files|personality profile]]. This describes the AI's likes, loves, family, dog... anything you like. This is one of the main mechanisms for differentiating behaviour between agents - thus allowing a player's actions to affect multiple agents in multiple ways, and allows for complex behaviour. The mechanism for doing this is the Personality Requirement.<br />
<br />
Here's a snippet of a Personality Profile.<br />
<syntaxhighlight source lang="lua" line start=5><br />
{ data = { "HipHop", "Pop"}, tags = { "music", "likes" } },<br />
{ data = { "Rock"}, tags = { "music", "dislikes" } },<br />
{ data = { "Classical", "HipHop"}, tags = { "music", "relaxes" } },<br />
{ data = { "LiverpoolReds" }, tags = { "sport", "likes", "celebrates" } },<br />
</syntaxhighlight><br />
<br />
So, this AI considers that HipHop & Pop are both music, and they like it. They consider Rock to be music, but they dislike it. They consider Classical and HipHop to be music that relaxes them. They consider LiverpoolReds to be related to sport, they like it, and they celebrate it.<br />
<br />
=== Personality Requirement ===<br />
<br />
{| class="wikitable"<br />
!colspan="3" | Personality Requirement<br />
|-<br />
! Name !! Type !! Description<br />
|-<br />
| ''subject'' || string || The primary tag that this requirement is seeking.<br />
|-<br />
| ''value'' || string || Optional. The value that has a tag matching ''subject''.<br />
|-<br />
| ''other'' || string || Optional. Another tag, or list of tags, that the value must match with for this requirement to be satisfied.<br />
|-<br />
| ''inverse'' || boolean || Defaults to false. Setting to true swaps the result of the requirement - so a match means the requirement fails, and the lack of a match means the requirement passes.<br />
|}<br />
<br />
<br />
<br />
The only mandatory part of a requirement is the subject. The subject is merely a tag, but it's the tag we look for first, and it's the tag that can be specified by [[AI Lua API#Change Subject|API call ChangeSubject]]. Let's write a requirement that will pass using only the subject.<br />
<syntaxhighlight source lang="lua" line start=5><br />
personalityRequirement = { subject = "music" },<br />
</syntaxhighlight><br />
This requirement, wherever it's used, will pass if the subject of "music" is set to "HipHop", "Pop", "Rock", or "Classical". So for instance, we might trigger a Dance Action if music is set to any of these. But that might not make a huge amount of sense for this AI, because they're not so keen on Rock music. So let's add an extra tag that we need for this requirement to be satisfied.<br />
<syntaxhighlight source lang="lua" line start=5><br />
personalityRequirement = { subject = "music", other = "likes" },<br />
</syntaxhighlight><br />
This means the requirement will no longer pass for "Classical" or "Rock", because they aren't tagged as "likes" in their profile. That makes more sense! But... do we want the AI to perform the same dance to "HipHop" ''and'' "Rock"? Possibly not. This is where the value attribute is useful.<br />
<syntaxhighlight source lang="lua" line start=5><br />
personalityRequirement = { value = "Rock", subject = "music", other = "likes" },<br />
</syntaxhighlight><br />
This requirement only passes for agents who like Rock music.<br />
<br />
Finally, what's inverse for? Essentially, it's for checking for the absence of something. Consider the requirement<br />
<syntaxhighlight source lang="lua" line start=5><br />
personalityRequirement = { value = "ManchesterBlues", subject = "celebrates", inverse = true },<br />
</syntaxhighlight><br />
Well spotted - "ManchesterBlues" is not present in our profile snippet. This means that, without the inverse flag, this requirement would fail. But with it, it passes! So, supposing we had a radio which set the subject as "celebrates", and the value as "ManchesterBlues", we could get this AI to sob gently to himself, while the one stood next to him is overcome with joy.<br />
<br />
== Statistics ==<br />
Each statistic is a tracked, saved, numerical value that represents a particular aspect of the AI. The value is clamped between 0 and 1. Each stat has two lists, above and below, of names and thresholds. These will become world states that become true when the value becomes greater or equal/lesser or equal (respectively) to the threshold value. Statistics can be adjusted by [[#Actions|actions]], [[#Responses|responses]], and [[#Reactions|reactions]]. In doing so the [[#World State|world state]] may change, and new [[#Goals|goals]] become achievable.<br />
<br />
Personality Effects will be referred to throughout this, so let's dig into them here.<br />
{| class="wikitable"<br />
!colspan="3" | Personality Effect<br />
|-<br />
! Name !! Type !! Description<br />
|-<br />
| ''stat'' || string || The unique name of the stat.<br />
|-<br />
| ''adjust'' || number || The value to be added to the current value of this stat. Use a negative number to reduce it!<br />
|}<br />
One of the simpler tables in the Agent Definition. Simply put, when this effect happens, the named ''stat'' will have ''adjust'' added to it. The stat will then be clamped in the range 0 - 1, and the world states that rely on it will be recalculated.<br />
<br />
=== Usage / Intention ===<br />
Personality Effects may or may not be a great name for these, but we are stuck with them! You may consider them to be side effects or secondary effects - things that happen as a result of an Action, that aren't to be taken into account when planning. Also, because they act upon statistics ranging from 0-1, the effects can be gradual.<br />
A simple example would be a Soda machine. An Agent may plan to use the soda machine because they are thirsty, because they need energy, because they are bored - or a combination. All valid use cases. However, a side effect of drinking is the need to go to the toilet! Nobody has a drink with the aim of going to the toilet, but it's something that happens. So a Soda machine could quite feasibly stop an Agent from being thirsty (so a requirement of "isThirsty" = true, and an effect of "isThirsty" = false). But you might add a personalityEffect of "bladder-o-meter" adjust = 0.2. Thus, each time an Agent uses the Soda machine, their bladder-o-meter is incremented by 0.2. Depending on the threshold of the bladder-o-meter stat, a world state change will eventually happen, and the Agent will have to consider going to the toilet - depending on the priority of the toilet goal, of course!<br />
<br />
= File Format =<br />
The definition is a single table, named Agent, containing several tables that define different aspects of an Agent.<br />
<br />
== Fails ==<br />
This table contains a string or strings that are turned into [[AI_Gestures]] and used when the Agent no longer has a valid goal. So if you add "Yawn", and see your agent yawning, constantly, they probably don't have anything better to do! Ensure you type the gesture precisely - it's case sensitive.<br />
<br />
== World State ==<br />
The World State is a description of everything an AI knows about, in the context of planning. It is simply a [[#GOAP State|GOAP State]].<br />
<br />
It also exist some special worlds states that add some features to the agent.<br />
{| class="wikitable"<br />
!colspan="3" | Personality Effect<br />
|-<br />
! Name !! Value!! Description<br />
|-<br />
| hasFlashlight || true || Adds a flashlight prop for the agent's character to use.<br />
|}<br />
<br />
== Goals ==<br />
A Goal is a state that an Agent desires to be in. The planner will seek to use the Actions at its disposal to come up with a plan (set of Actions) that it can run to adjust the current World State so that it includes the Goal state. At its heart is a [[#GOAP State|GOAP State]], but it has some extra wizardry too.<br />
{| class="wikitable"<br />
!colspan="3" | Personality Effect<br />
|-<br />
! Name !! Type !! Description<br />
|-<br />
| ''goal'' || table || A [[#GOAP State|GOAP State]].<br />
|-<br />
| ''interrupts'' || boolean || Defaults to false. When set to true, this Goal will interrupt any of lower priority if it becomes achievable. For instance, chasing the player is more important than eating a snack.<br />
|-<br />
| ''priority'' || number || The higher the priority a goal is, the more important it is. As a result it will be attempted before lower priority goals.<br />
|-<br />
| ''onCompletion'' || table || Optional. This is a [[#GOAP State|GOAP State]] that will be applied to the [[#World State|World State]] when this goal is successful. Useful for cyclic tasks (e.g. patrolling).<br />
|}<br />
<br />
So the goal state is what we would like our world state to include (it doesn't have to be an exhaustive list of all the state items we know about). Any difference between goal and world mean it is a candidate for planning, where we try to use Actions we have that we are able to perform to turn our world state into the goal state. If the goal interrupts, it means that the agent will stop what its doing if it's suddenly possible for this goal to be achieved.<br />
<br />
The onCompletion state is useful for undoing changes made in the course of planning (or 'unlocking' state for another goal). So it might be that once your AI has patrolled you reset "patrolled" back to false so that it can patrol again.<br />
<br />
== Stats ==<br />
List of [[#Statistics|Statistics]].<br />
Statistics are adjusted by PersonalityEffects. They're used to change the world state in a gradual way - so you might have an action that slowly makes an agent more and more tired, eventually triggering a change in world state that allows new goals to be planned for. Each stat value can be created on the agent definition or on the respective [[Character Profiles#Character personality files|character personality file]], on the agent definition it’s possible to set the stat default value, the above and below fields; on the character personality it’s only possible to set its value but that value will have priority, i.e. if the value is defined on both then the stat value will be the same as the one set by the personality file.<br />
<br />
{| class="wikitable"<br />
!colspan="3" | Statistic Table<br />
|-<br />
! Name !! Type !! Description<br />
|-<br />
| ''name'' || string || Unique name for this statistic.<br />
|-<br />
| ''default'' || number || Default value for this statistic.<br />
|-<br />
| ''above'' || table || List of states that will become true when above or equal to the specified threshold (see next table).<br />
|-<br />
| ''below'' || table || List of states that will become true when below or equal to the specified threshold (see next table).<br />
|}<br />
<br />
{| class="wikitable"<br />
!colspan="3" | Statistic-State Table<br />
|-<br />
! Name !! Type !! Description<br />
|-<br />
| ''id'' || string || The name of the world state to be created.<br />
|-<br />
| ''threshold'' || number || Threshold for this statistic. Behaviour depends upon whether this state is in the above or below table.<br />
|}<br />
<br />
So, what might this look like?<br />
<syntaxhighlight source lang="lua" line start=5><br />
{<br />
name = "happiness",<br />
default = 0.5,<br />
above = {<br />
{ id = "elated", threshold = 1.00 },<br />
{ id = "cheerful", threshold = 0.8 }<br />
}<br />
},<br />
</syntaxhighlight><br />
This stat is called happiness. It starts at 0.5. It has two world states, "elated", which becomes true at maximum happiness (1.0), and "cheerful", which happens when it's merely 0.8.<br />
<syntaxhighlight source lang="lua" line start=8><br />
{<br />
name = "energy",<br />
default = 0.5,<br />
above = { id = "energized", threshold = 1.0 },<br />
below = { id = "tired", threshold = 0.0 }<br />
},<br />
</syntaxhighlight><br />
Notice that this one has a single entry in both above and below, missing out the nested brackets.<br />
<br />
== Actions ==<br />
An Actions is something that the AI '''does'''. In order to '''do''' it, it must have a particular world state. After having '''done''' it, it will change its world state.<br />
{| class="wikitable"<br />
!colspan="5" | Action Table<br />
|-<br />
! Name !! Type !! Required !! Default Value !! Description<br />
|-<br />
| ''name'' || string || style="text-align:center;"| ✓ || || The name of the Action, which should be unique.<br />
|-<br />
| ''gesture'' || string || || || The gesture to play when performing this Action.<br />
|-<br />
| ''audio'' || string || style="text-align:center;"| ✓ || || The Wwise event to play when performing this Action.<br />
|-<br />
| ''effect'' || table || style="text-align:center;"| ✓ || || The effect GOAP State. This will be applied to the World State when this Action is performed.<br />
|-<br />
| ''required'' || table || style="text-align:center;"| ✓ || || The required GOAP State. The World State must include this state in order for the Action to be used.<br />
|-<br />
| ''speed'' || number || || style="text-align:center;"| 1 ||The agent velocity to reach a determined position, if it requires to be in a specific location to be performed.<br />
|-<br />
| ''range'' || number || || style="text-align:center;"| 0.5 || The distance between the agent and a certain target, if it requires to be in a specific location to be performed.<br />
|-<br />
| ''cost'' || number || || style="text-align:center;"| 1 || The cost to be performed, this should only be manually set to untie similar actions (with the same "goal", "effect" and "required") on the calculation of the agent plan.<br />
|-<br />
| ''personalityEffect'' || table || || ||The effect on personality stats that performing this Action has.<br />
|-<br />
| ''personalityRequirement'' || table || || || The required [[Character Profiles#Character personality files|personality profile]] this AI needs for this Action to run.<br />
|-<br />
| ''targetAgent'' || boolean || || style="text-align:center;"| False || If true, the Agent requires that there be another agent nearby for this Action to be performed.<br />
|-<br />
| ''targetPlayer'' || boolean || || style="text-align:center;"| False || If true, the Agent requires that the player be nearby for this Action to be performed.<br />
|-<br />
| ''data'' || table || || || When this Action happens, the data will be sent. The recipient of the data is held in the sending agent's worldstate. A state named {this action's name} with "DataRecipient" appended will be used for this. See the further explanation below.<br />
|}<br />
=== Sending Data as part of an Action ===<br />
This is where things become a little more complicated. Actions can result in an agent sending data. The data is fixed in the Agent profile, but the recipient is not - this is because this would mean this Action would require the presence of a particular device (which could be the mobile phone device of another agent). The solution to this issue is to store the name of this device in the world state (yes, states can hold data other than booleans!). The name of the state that this is stored in is derived from the name of the action itself.<br />
<br />
Let's consider the following Action:<br />
<syntaxhighlight source lang="lua" line start=5><br />
{<br />
name = "SendEmail",<br />
effect = { state = "hasMotivation", value = true },<br />
data = {<br />
internalName = "AI Email",<br />
name = "Data Name",<br />
description = "A description of this name",<br />
immutable = true,<br />
dataType = 3,<br />
creatorName = "Top Secret Source",<br />
dataString = "All Your Base Are Belong To Us",<br />
},<br />
},<br />
</syntaxhighlight><br />
<br />
First we must work out where this is going to be sent, and change the relevant state within the AI that is performing the Action! The name of the Action is "SendEmail". The state that will be inspected to find the recipient is this name, with "DataRecipient" appended to it. So the state to store it in is "SendEmailDataRecipient". Let's see what this looks like for an example AI - in this case the AI is called "Edward", and the recipient is called "Julian":<br />
<syntaxhighlight source lang="lua" line start=65><br />
AI.AlterNPCWorldState("Edward", "SendEmailDataRecipient", "Julian")<br />
</syntaxhighlight><br />
Now, if we were to inspect Edward's AI state, we would see that the state "SendEmailDataRecipient" is now set to the string, "Julian". This will be used by the Action. If and when Edward runs this Action, this data will be sent from him to Julian. If this state is not set, the Action will still run, but the data will not be sent (because there is nowhere for it to go).<br />
<br />
== Special Actions ==<br />
There are a number of special actions with specific types of behaviour. These actions are set as any normal action, but it have some differences: to add a specific special action its name must be exactly the same as any of the actions mentioned on the table below, some action's parameters are hardcoded and can't be changed in the agent file, so even if they aren't in the Lua file they will be added automatically, this is because some parameters could break some more specific actions or alter the intended behaviour, these parameters on each special action can be seen in the next table m as "Unchangeable parameters added automatically". Other variables like "targetAgent" and "targetPlayer" never affect the behaviour of these actions and they should be only used in a generic action.<br />
<br />
{| class="wikitable"<br />
!colspan="4" | Special action Table<br />
|-<br />
! Name !! Precondition !! Unchangeable Parameters Added Automatically !! Description<br />
|-<br />
| ''PatrolAction'' || Patrol points must be defined on the mission script characters definition. Check [[Character Profiles#Enemies|here]] for more information. || style="text-align:center;"| - || To move the agent between defined points. Motivation is subtracted when arriving on a patrol point.<br />
|-<br />
| ''CatchIntruderAction'' || The intruder is visible. || required = <br /> { state = "intruderVisible", value = true } || Action to get closer to the player (to caught it with the help of another action), if it is visible, or its last known position is known.<br />
|-<br />
| ''TaserAction'' || | The intruder is visible. || required = <br /> { state = "intruderVisible", value = true } || The player is tased and caught with this action, if the player is within range.<br />
|-<br />
| ''DefaultAttackAction'' || The intruder is visible. || required = <br /> { state = "intruderVisible", value = true } || The player is attacked and caught with a melee attack, if the player is within range.<br />
|-<br />
| ''SearchIntruderAction'' || The level must contain one or more search points (i.e. gameobjects with the SearchPoint component). || required = <br /> { state = "intruderSpotted", value = true } <br /><br /> effect = <br /> { state = "intruderVisible", value = true} || This action searches for the player, if the player has been seen but is no longer visible.<br />
|-<br />
| ''InvestigateAction'' || Some untrusted was heard. || required = <br /> { state = "heardUntrustedNoise", value = false }, -- It means that the guard already check the origin of the untrusted noise [Only for readability]|| Action to investigate a noise position if it's a non trusted sound.<br />
|-<br />
| ''RestAction'' || style="text-align:center;"| - || required = <br /> { { state = "usedSitting", value = false }, <br /> { state = "canUseSitting", value = false } } <br /><br /> effect = <br /> { state = "sleepy", value = false} || Rest for a bit in the closest chair, if the agent is also sleepy then it'll fall asleep as well.<br />
|}<br />
<br />
<br />
Like mentioned above certain special actions need specific GOAP states, these states are also special because its values are updated automatically in the game AI code, depending on the state of the game.<br />
<br />
{| class="wikitable"<br />
!colspan="2" | Required GOAP State Table<br />
|-<br />
! Name !! Description<br />
|-<br />
| ''intruderVisible'' || If true, the player is visible in the field vision of the agent<br />
|-<br />
| ''intruderSpotted'' || If true, the player was spotted and the agent is trying to catch it<br />
|-<br />
| ''heardUntrustedNoise'' || If true, the agent heard a non trusted sound<br />
|-<br />
| ''unreadMessages'' || If true, the agent have unread messages<br />
|}<br />
<br />
=== Reactions to Data ===<br />
Agents can also react to data, and the key to this is the (optional) metadata within the DataPoint. This metadata can be compared with an Agent's personality, and Reactions can occur in much the same way.<br />
<br />
Let's look at the following DataPoint table, that might appear in a mission script:<br />
<syntaxhighlight source lang="lua"><br />
CoffeeOffer = {<br />
internalName = "CoffeeOffer",<br />
name = "theapostle_data_Coffee_name",<br />
dataType = 1,<br />
creatorName = "Baltar Beans",<br />
description = "text/UTF8",<br />
dataColor = {1.0, 1.0, 1.0, 1.0},<br />
meta = { { data = { "coffee" }, tags = { "drink" } } },<br />
},<br />
</syntaxhighlight><br />
Notice the meta table on the end. This tells the AI that the data is about "coffee", and that "coffee" is a "drink". How could we make use of this in a level?<br />
<br />
Let's make a Reaction that makes use of this.<br />
<syntaxhighlight source lang="lua" line start=5><br />
{<br />
personalityEffect = { stat = "thirst", adjust = 0.5 },<br />
personalityRequirement = { subject = "drink", other = "likes" },<br />
}<br />
</syntaxhighlight><br />
This Reaction is looking for something that is tagged as a drink, and that the agent likes. To complete this example, we need to inspect some personality files.<br />
<br />
This agent will React to this DataPoint, because they know that coffee is a drink, and they like it.<br />
<syntaxhighlight source lang="lua" line start=5><br />
{ data = { "coffee", "tea"}, tags = { "drink", "likes" } },<br />
</syntaxhighlight><br />
This AI won't, because while they know coffee is a drink, they don't like it (it's tagged as "dislikes").<br />
<syntaxhighlight source lang="lua" line start=5><br />
{ data = { "coffee"}, tags = { "drink", "dislikes" } },<br />
</syntaxhighlight><br />
...and nor will this one. This AI doesn't even know what coffee is.<br />
<syntaxhighlight source lang="lua" line start=5><br />
{ data = { "tea"}, tags = { "drink", "likes" } },<br />
</syntaxhighlight><br />
<br />
<br />
== Interests ==<br />
An Interest may be a Device, or may simply be a particular part of a level that an AI needs to get to in order to perform an Action. An Interest is created by adding an InterestPoint to a GameObject (or making a new GameObject with an InterestPoint added). Be sure to orient the GameObject such that the Z axis is pointing in the direction the AI should use the InterestPoint from.<br />
<br />
Note that each Interest may define its own World State which will be added to any AI able to use it - thereby guaranteeing that any adjustments the Device makes to AI using it are valid.<br />
=== canUse ===<br />
This is a using Action. The Agent will attempt to reach the nearest Interest that they know to be working, and try to use it. If it's in an Amok state, this may fail. The table is pretty similar to a standard [[#Action|Action]].<br />
{| class="wikitable"<br />
!colspan="2" | canUse Table<br />
|-<br />
! Name !! Description<br />
|-<br />
| ''interest'' || The name of the InterestPoint this Action will take place at.<br />
|-<br />
| ''effect'' || The effect [[#GOAP State|GOAP State]]. This will be applied to the World State when this Action is performed.<br />
|-<br />
| ''required'' || The required [[#GOAP State|GOAP State]]. The World State must include this state in order for the Action to be used.<br />
|-<br />
| ''personalityEffect'' || Optional. The [[#Statistics|effect]] on personality stats that performing this Action has.<br />
|-<br />
| ''personalityRequirement'' || Optional. For this Action to be performed, this [[#Personality|requirement]] must be satisfied.<br />
|-<br />
| ''gesture'' || Optional. This is the gesture to be played when the agent uses a working instance of this Interest.<br />
|-<br />
| ''gestureAmok'' || Optional. This is the gesture to be played when the agent uses an instance of this Interest that is in its Amok state.<br />
|}<br />
<br />
==== World State ====<br />
Adding a canUse to an Agent also adds a state to its world state, in the form of "usedXXX", where "XXX" is the name of the interest; so an agent able to use a "Printer" will have a "usedPrinter" state, initially set to false. This is useful to create a sequence of "uses", perhaps within a goal where each state is reset upon completion.<br />
= Case Study =<br />
Brace yourselves for a long, long example from the game, with some discussion afterwards.<br />
<br />
== Guard Example ==<br />
The Guard is the standard 'enemy' AI currently in the game. Please be aware that the game is still in development and there may (will!) be bugs in this.<br />
<syntaxhighlight source lang="lua" line start=5><br />
<br />
Agent =<br />
{<br />
fails =<br />
{<br />
"WaitingHandsOnHips",<br />
},<br />
<br />
world = -- What they know on the beginning<br />
{<br />
-- [Automatically added] --<br />
{ state = "heardUntrustedNoise", value = false }, -- NPC heard some untrust noise? [This state exists on all NPCs. Will be added if missing] --<br />
{ state = "intruderVisible", value = false }, -- Player is visible? [This state exists on all NPCs. Will be added if missing] --<br />
{ state = "intruderSpotted", value = false }, -- Player was visible and SearchIntruderAction still wasn't performed? [This state exists on all NPCs. Will be added if missing] --<br />
<br />
-- [NOT automatically added] --<br />
{ state = "patrolCompleted", value = false }, -- To perform the patrolAction<br />
{ state = "intruderInRange", value = false }, -- If true the CatchIntruderAction was peformed successfully<br />
{ state = "hasPrisoner", value = false }, -- The main objective of this AI<br />
},<br />
<br />
stats =<br />
{<br />
{<br />
name = "motivation",<br />
default = 0.5,<br />
above = { id = "hasMotivation", threshold = 0.01 }<br />
},<br />
<br />
{<br />
name = "energy",<br />
default = 0.5,<br />
above =<br />
{<br />
{ id = "energized", threshold = 1.0 },<br />
},<br />
below =<br />
{<br />
{ id = "tired", threshold = 0.4 },<br />
{ id = "exhausted", threshold = 0.0 }<br />
}<br />
},<br />
},<br />
<br />
goals =<br />
{<br />
---- MainGoal -------<br />
{<br />
states =<br />
{<br />
{ state = "hasPrisoner", value = true }, -- Order of wanted events : IntruderVisible = true -> IntruderInRange = true -> hasPrisoner == true<br />
},<br />
interrupts = true,<br />
priority = 100,<br />
},<br />
<br />
----- Investigate the unstrusted noise --<br />
{<br />
states =<br />
{<br />
{ state = "heardUntrustedNoise", value = false },<br />
},<br />
interrupts = true,<br />
priority = 99,<br />
},<br />
<br />
-- Energy goals --<br />
{<br />
states =<br />
{<br />
{ state = "exhausted", value = false },<br />
},<br />
priority = 51,<br />
},<br />
<br />
{<br />
states =<br />
{<br />
{ state = "tired", value = false },<br />
},<br />
priority = 50,<br />
},<br />
<br />
-- Motivation goal --<br />
{<br />
states =<br />
{<br />
{ state = "hasMotivation", value = true },<br />
},<br />
priority = 25,<br />
},<br />
<br />
<br />
-- Patrol goal --<br />
{<br />
states =<br />
{<br />
{ state = "patrolCompleted", value = true },<br />
},<br />
priority = 10,<br />
onCompletion =<br />
{<br />
{ state = "patrolCompleted", value = false }, -- To be able to patrol always<br />
}<br />
},<br />
},<br />
<br />
actions =<br />
{<br />
-- [Special action] --<br />
-- Patrol in the points defined in the mission character's table --<br />
{<br />
name = "PatrolAction",<br />
effect = { state = "patrolCompleted", value = true},<br />
range = 0, -- Actions default value [Only for readability]<br />
speed = 1, -- Actions default value [Only for readability]<br />
},<br />
<br />
-- [Special action] --<br />
-- Agent is going to investigate the position of the heard noise to see if is going to find the intruder --<br />
{<br />
name = "InvestigateAction",<br />
effect =<br />
{<br />
{ state = "heardUntrustedNoise", value = false }, -- It means that the guard already check the origin of the untrusted noise [Only for readability]<br />
},<br />
required =<br />
{<br />
{ state = "hasPrisoner", value = false} -- To not investigate a sound right after the player is captured --<br />
},<br />
range = 2,<br />
speed = 2, -- Jog speed (should be speed = "Jog")<br />
npcStatusIcon = "Confused",<br />
},<br />
<br />
-- [Special action] --<br />
-- Agent is going to search for the player in the level SearchPoints if it was spotted, if the action is done successfully it means that the intruder is now visible --<br />
{<br />
name = "SearchIntruderAction",<br />
effect =<br />
{<br />
{ state = "intruderVisible", value = true}, -- Objective of this action is for the intruder to be visible again [Only for readability since this is always added when using this action] --<br />
},<br />
required =<br />
{<br />
{ state = "intruderSpotted", value = true }, -- [Only for readability since this is always added when using this action] --<br />
{ state = "heardUntrustedNoise", value = false }, -- If the agent heards a untrusted noise after losing the player then the investigate action should have proprioty because it could indicate the player's position better than the SearchAction //<br />
},<br />
range = 1,<br />
speed = 2, -- Jog speed<br />
npcStatusIcon = "Confused",<br />
},<br />
<br />
-- [Special action] --<br />
-- The objective is be in range of the player to attack it --<br />
{<br />
name = "CatchIntruderAction",<br />
effect =<br />
{<br />
{ state = "intruderInRange", value = true} -- State to indicate that the agent is near the player<br />
},<br />
required =<br />
{<br />
{ state = "intruderVisible", value = true }, -- The player needs to be visible to use this action [Only for readability since this is always added when using this action] --<br />
},<br />
range = 0, -- Actions default value [Only for readability]<br />
speed = 3, -- Run speed<br />
npcStatusIcon = "Alert",<br />
},<br />
<br />
-- [Special action] --<br />
-- Attack the player with a taser --<br />
{<br />
name = "TaserAction",<br />
effect = { state = "hasPrisoner", value = true}, -- The main goal --<br />
required =<br />
{<br />
{ state = "intruderInRange", value = true },<br />
{ state = "intruderVisible", value = true }, -- The player needs to be visible to use this action [Only for readability since this is always added when using this action] --<br />
},<br />
range = 2,<br />
speed = 3, -- Run speed<br />
cost = 1, -- Actions default value [Only for readability]<br />
},<br />
<br />
-- [Special action] --<br />
-- Not fully functional, default action to attack the player without any prop --<br />
{<br />
name = "DefaultAttackAction", -- Specific action<br />
effect = { state = "hasPrisoner", value = true},<br />
required =<br />
{<br />
{ state = "intruderInRange", value = true },<br />
{ state = "intruderVisible", value = true }, -- The player needs to be visible to use this action [Only for readability since this is always added when using this action] --<br />
},<br />
range = 1.5,<br />
speed = 3, -- Run speed<br />
cost = 5, -- Cost is higher than normal because its the default attack action, the action with the lest cost will be one that will be used<br />
},<br />
<br />
<br />
-- [Special action] --<br />
-- Rest in a sittable object with an interest "chair" --<br />
{<br />
name = "RestAction",<br />
effect =<br />
{<br />
{state = "exhausted", value = false },<br />
{state = "sleepy", value = false }, -- If the agent is sleepy then it will sleep when resting [Only for readability since this is always added when using this action] --<br />
},<br />
personalityEffect = { stat = "energy", adjust = 0.5 },<br />
npcStatusIcon = "Resting",<br />
},<br />
<br />
-- Other actions --<br />
{<br />
name = "Yawn",<br />
gesture = "Yawn",<br />
effect = { state = "tired", value = false },<br />
personalityEffect = { stat = "energy", adjust = 0.3 },<br />
},<br />
},<br />
<br />
<br />
canUse =<br />
{<br />
{<br />
interest = "Soda",<br />
effect = { state = "hasMotivation", value = true },<br />
personalityEffect =<br />
{<br />
{ stat = "motivation", adjust = 1.0 },<br />
{ stat = "bladder", adjust = 0.1 },<br />
},<br />
gesture = "UseSodaMachine",<br />
npcStatusIcon = "Resting",<br />
},<br />
{<br />
interest = "Coffee",<br />
effect = { state = "hasMotivation", value = true },<br />
personalityEffect =<br />
{<br />
{ stat = "motivation", adjust = 1.0 },<br />
{ stat = "bladder", adjust = 0.1 },<br />
{ stat = "energy", adjust = 1.0 },<br />
},<br />
gesture = "UseCoffeeMachine",<br />
npcStatusIcon = "Resting",<br />
},<br />
<br />
{<br />
interest = "Snacks",<br />
effect = { state = "hasMotivation", value = true },<br />
personalityEffect = { stat = "motivation", adjust = 1.0 },<br />
gesture = "UseSnackMachine",<br />
npcStatusIcon = "Resting",<br />
},<br />
},<br />
}<br />
<br />
}<br />
</syntaxhighlight><br />
<br />
Phew. Let's go over the sections in turn.<br />
<br />
<br />
=== World State (world) ===<br />
In this section we have some states that are going to be added automatically on every NPC/agent, as well some that are set manually like the "patrolCompleted" with the value as false, to patrol when none other can be achieved or the "hasPrisoner" that is principal objective of the AI.<br />
<br />
=== Stats ===<br />
We define want we can call the two main states of every AI, "motivation" and "energy", some other stats can be defined in the character file or overwrite the values defined in the agent file. But is important to notice that these two states need to be defined in this agent because both use the above and below threshold, this is going to create some world states that are needed for our case.<br />
=== Goals ===<br />
The goals are want the agent wants to achieve, in our agent we have the following goals:<br />
* "hasPrisoner", true : The main goal of the AI that is used to catch the player.<br />
* "heardUntrustedNoise", false : If some untrusted sound is heard the agent will investigate the position of that sound, this goal is responsible for this.<br />
* "exhausted", false and "tired", false : The agent are going to try to increase the "energy" stat to achieve this goal<br />
* "hasMotivation", true : Very similar with the goal above, but in this case the stat responsible is the "motivation", this stat influence for example how many points the agent are going to search when doing the "SearchIntruderAction"<br />
* "patrolCompleted", true : Goal to achieve if all the above all impossible to, this is a loop goal, that means that as soon as the state "patrolCompleted" is true (by using the PatrolAction") this goal will set that same state as false, this can be seen in the "onCompletion" part of the goal, this is specially useful for having always a plan with achievable goals.<br />
<br />
=== Actions ===<br />
In here we have two different types of actions the ones that we call special, that are used to reach specific results with some unchangeable parameters that are added automatically, their behaviour is very rigid; and the other type of actions, are fully set in the agent file, like the "Yawn" action, it will play a gesture and adjust the "energy" stat when the "tired" state is true, it doesn't need a "required" parameter because is only going to be used when the "tired" state have the opposite value than the action's "effect", so "required = { state = "tired", value = true }" is not needed.<br />
<br />
=== canUse ===<br />
The first three Interests are methods of recovering motivation, which is reduced by patrolling. Each modifies an agent's stats in different ways. The next Interest is the Sink, which is used to cool down a player who has been burnt by a malevolent coffee machine. The final Interest is the Toilet, which hopefully needs no explanation!<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=VSCode_Snippets&diff=1935VSCode Snippets2024-01-31T15:57:47Z<p>WikiAdmin: </p>
<hr />
<div>= Snippets file for Visual Studio Code =<br />
Copy & paste the following code into your Snippets file in VisualStudio Code to add autocompletion for Off Grid's Lua API.<br />
<syntaxhighlight source lang="typescript"><br />
<br />
"AI.Pause": {<br />
"prefix": "AI.Pause",<br />
"body": [<br />
"AI.Pause(${0:characterName})"<br />
],<br />
"description": "Pauses the agent, and stops it from doing anything."<br />
},<br />
"AI.Unpause": {<br />
"prefix": "AI.Unpause",<br />
"body": [<br />
"AI.Unpause(${0:characterName})"<br />
],<br />
"description": "Unpauses a hidden agent, resuming standard behaviour."<br />
},<br />
"AI.IsPaused": {<br />
"prefix": "AI.IsPaused",<br />
"body": [<br />
"AI.IsPaused(${0:characterName})"<br />
],<br />
"description": "Returns a bool based on if a character is currently paused or not"<br />
},<br />
"AI.AddGoal": {<br />
"prefix": "AI.AddGoal",<br />
"body": [<br />
"AI.AddGoal(${0:characterName}, ${1:goal})"<br />
],<br />
"description": "Adds a defined goal"<br />
},<br />
"AI.RemoveGoal": {<br />
"prefix": "AI.RemoveGoal",<br />
"body": [<br />
"AI.RemoveGoal(${0:characterName}, ${1:goalName})"<br />
],<br />
"description": "Removes any defined goals"<br />
},<br />
"AI.AddTemporaryGoal": {<br />
"prefix": "AI.AddTemporaryGoal",<br />
"body": [<br />
"AI.AddTemporaryGoal(${0:characterName}, ${1:goal})"<br />
],<br />
"description": "Adds a goal, taking top priority, to the named AI"<br />
},<br />
"AI.GetNPCProfileByTag": {<br />
"prefix": "AI.GetNPCProfileByTag",<br />
"body": [<br />
"AI.GetNPCProfileByTag(${0:characterName}, ${1:tag})"<br />
],<br />
"description": "Get all values of a tag from a chaarcter's profile, if the tag exists."<br />
},<br />
"AI.Lua_CheckNPCProfile": {<br />
"prefix": "AI.Lua_CheckNPCProfile",<br />
"body": [<br />
"AI.Lua_CheckNPCProfile(${0:characterName}, ${1:tag}, ${2:value})"<br />
],<br />
"description": "Check if character's profile data contains a specific tag wiht a specific value"<br />
},<br />
"AI.GetNPCStat": {<br />
"prefix": "AI.GetNPCStat",<br />
"body": [<br />
"AI.GetNPCStat(${0:characterName}, ${1:statName})"<br />
],<br />
"description": "Returns current value of NPC's stat"<br />
},<br />
"AI.SetNPCStat": {<br />
"prefix": "AI.SetNPCStat",<br />
"body": [<br />
"AI.SetNPCStat(${0:characterName}, ${1:statName}, ${2:newValue})"<br />
],<br />
"description": "Sets an NPC's AI stat to absolute value"<br />
},<br />
"AI.AlterNPCStat": {<br />
"prefix": "AI.AlterNPCStat",<br />
"body": [<br />
"AI.AlterNPCStat(${0:characterName}, ${1:statName}, ${2:statDelta})"<br />
],<br />
"description": "Alters an NPC's AI stat by relative value"<br />
},<br />
"AI.AlterNPCWorldState": {<br />
"prefix": "AI.AlterNPCWorldState",<br />
"body": [<br />
"AI.AlterNPCWorldState(${0:characterName}, ${1:state}, ${2:value})"<br />
],<br />
"description": "Change the World State of an NPC."<br />
},<br />
"AI.FavourInterest": {<br />
"prefix": "AI.FavourInterest",<br />
"body": [<br />
"AI.FavourInterest(${0:characterName}, ${1:device}, ${2:permanent})"<br />
],<br />
"description": "Reduce the cost of an AI using a particular Interest"<br />
},<br />
"AI.AvoidInterest": {<br />
"prefix": "AI.AvoidInterest",<br />
"body": [<br />
"AI.AvoidInterest(${0:characterName}, ${1:device}, ${2:permanent})"<br />
],<br />
"description": "Increase the cost of an AI using a particular Interest"<br />
},<br />
"AI.SetNPCFavouredComputer": {<br />
"prefix": "AI.SetNPCFavouredComputer",<br />
"body": [<br />
"AI.SetNPCFavouredComputer(${0:characterName}, ${1:computer})"<br />
],<br />
"description": "Set NPC's computer, this will be used for a variety of actions"<br />
},<br />
"AI.AddAction": {<br />
"prefix": "AI.AddAction",<br />
"body": [<br />
"AI.AddAction(${0:characterName}, ${1:action})"<br />
],<br />
"description": "Adds an action that can be used immediately"<br />
},<br />
"AI.RemoveAction": {<br />
"prefix": "AI.RemoveAction",<br />
"body": [<br />
"AI.RemoveAction(${0:characterName}, ${1:actionName})"<br />
],<br />
"description": "Removes any action"<br />
},<br />
"Animator.SetBool": {<br />
"prefix": "Animator.SetBool",<br />
"body": [<br />
"Animator.SetBool(${0:missionObjectName}, ${1:parameterName}, ${2:value})"<br />
],<br />
"description": "Sets a bool parameter by name"<br />
},<br />
"Animator.SetFloat": {<br />
"prefix": "Animator.SetFloat",<br />
"body": [<br />
"Animator.SetFloat(${0:missionObjectName}, ${1:parameterName}, ${2:value})"<br />
],<br />
"description": "Sets a float parameter by name"<br />
},<br />
"Animator.SetInt": {<br />
"prefix": "Animator.SetInt",<br />
"body": [<br />
"Animator.SetInt(${0:missionObjectName}, ${1:parameterName}, ${2:value})"<br />
],<br />
"description": "Sets a int parameter by name"<br />
},<br />
"Animator.SetTrigger": {<br />
"prefix": "Animator.SetTrigger",<br />
"body": [<br />
"Animator.SetTrigger(${0:missionObjectName}, ${1:parameterName})"<br />
],<br />
"description": "Begins a trigger parameter by name"<br />
},<br />
"Animator.GetBool": {<br />
"prefix": "Animator.GetBool",<br />
"body": [<br />
"Animator.GetBool(${0:missionObjectName}, ${1:parameterName})"<br />
],<br />
"description": "Gets the current value of a bool parameter by name"<br />
},<br />
"Animator.GetFloat": {<br />
"prefix": "Animator.GetFloat",<br />
"body": [<br />
"Animator.GetFloat(${0:missionObjectName}, ${1:parameterName})"<br />
],<br />
"description": "Gets the current value of a float parameter by name"<br />
},<br />
"Animator.GetInt": {<br />
"prefix": "Animator.GetInt",<br />
"body": [<br />
"Animator.GetInt(${0:missionObjectName}, ${1:parameterName})"<br />
],<br />
"description": "Gets the current value of a int parameter by name"<br />
},<br />
"Apps.RequestAppState": {<br />
"prefix": "Apps.RequestAppState",<br />
"body": [<br />
"Apps.RequestAppState(${0:appName}, ${1:newState})"<br />
],<br />
"description": "Ask an App to change it's state"<br />
},<br />
"Apps.Console_OutputLine": {<br />
"prefix": "Apps.Console_OutputLine",<br />
"body": [<br />
"Apps.Console_OutputLine(${0:appName}, ${1:outputString})"<br />
],<br />
"description": "Output a line to the terminal UI"<br />
},<br />
"SetState": {<br />
"prefix": "SetState",<br />
"body": [<br />
"SetState(${0:newState})"<br />
],<br />
"description": "Sets the App's state."<br />
},<br />
"CreateStatusWindow": {<br />
"prefix": "CreateStatusWindow",<br />
"body": [<br />
"CreateStatusWindow()"<br />
],<br />
"description": "Create the status window"<br />
},<br />
"RemoveStatusWindow": {<br />
"prefix": "RemoveStatusWindow",<br />
"body": [<br />
"RemoveStatusWindow()"<br />
],<br />
"description": "Remove this app's status window"<br />
},<br />
"DisplayStatusWindow": {<br />
"prefix": "DisplayStatusWindow",<br />
"body": [<br />
"DisplayStatusWindow(${0:enabled})"<br />
],<br />
"description": "Show or hide the app's status window."<br />
},<br />
"UpdateStatusWindow": {<br />
"prefix": "UpdateStatusWindow",<br />
"body": [<br />
"UpdateStatusWindow(${0:text})"<br />
],<br />
"description": "Update the text content of the app's status window"<br />
},<br />
"SetStatusIcon": {<br />
"prefix": "SetStatusIcon",<br />
"body": [<br />
"SetStatusIcon(${0:id})"<br />
],<br />
"description": "Set the icon used for the app's status window"<br />
},<br />
"SetStatusIconColor": {<br />
"prefix": "SetStatusIconColor",<br />
"body": [<br />
"SetStatusIconColor(${0:color})"<br />
],<br />
"description": "Set the color of the status window icon"<br />
},<br />
"CreateAppWindow": {<br />
"prefix": "CreateAppWindow",<br />
"body": [<br />
"CreateAppWindow(${0:title}, ${1:backgroundImagePath})"<br />
],<br />
"description": "Initialize the AppWindow for this App"<br />
},<br />
"RemoveAppWindow": {<br />
"prefix": "RemoveAppWindow",<br />
"body": [<br />
"RemoveAppWindow()"<br />
],<br />
"description": "Reset the AppWindow"<br />
},<br />
"ShowAppWindow": {<br />
"prefix": "ShowAppWindow",<br />
"body": [<br />
"ShowAppWindow()"<br />
],<br />
"description": "Make the AppWindow visible (rember to create the window first!)"<br />
},<br />
"HideAppWindow": {<br />
"prefix": "HideAppWindow",<br />
"body": [<br />
"HideAppWindow()"<br />
],<br />
"description": "Hide the app window."<br />
},<br />
"SetAppWindowContent": {<br />
"prefix": "SetAppWindowContent",<br />
"body": [<br />
"SetAppWindowContent(${0:content})"<br />
],<br />
"description": "Set the content text of the AppWindow"<br />
},<br />
"ShowAppWindowProgress": {<br />
"prefix": "ShowAppWindowProgress",<br />
"body": [<br />
"ShowAppWindowProgress(${0:display})"<br />
],<br />
"description": "Show (or hide) a progress bar on the AppWindow"<br />
},<br />
"SetAppWindowProgress": {<br />
"prefix": "SetAppWindowProgress",<br />
"body": [<br />
"SetAppWindowProgress(${0:progress})"<br />
],<br />
"description": "Set the progres on the AppWindow progress bar (remember to use 'ShowAppWindowProgress(true)' first to enable it!)"<br />
},<br />
"StartLuaCoroutine": {<br />
"prefix": "StartLuaCoroutine",<br />
"body": [<br />
"StartLuaCoroutine(${0:dynValue})"<br />
],<br />
"description": "..."<br />
},<br />
"Color.RandomColor": {<br />
"prefix": "Color.RandomColor",<br />
"body": [<br />
"Color.RandomColor(${0:alpha})"<br />
],<br />
"description": "Returns a random color"<br />
},<br />
"Color.LessRandomColor": {<br />
"prefix": "Color.LessRandomColor",<br />
"body": [<br />
"Color.LessRandomColor(${0:hMin}, ${1:hMax}, ${2:sMin}, ${3:sMax}, ${4:vMin}, ${5:vMax}, ${6:aMin}, ${7:aMax})"<br />
],<br />
"description": "Returns a random color based on hue, saturation, value, and alpha upper and lower limits"<br />
},<br />
"Color.Clear" : {<br />
"prefix": "Color.Clear",<br />
"body": [<br />
"Color.Clear"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #ffffff;\"| || 1, 1, 1\r\n|-\r\n|}"<br />
},<br />
"Color.Black" : {<br />
"prefix": "Color.Black",<br />
"body": [<br />
"Color.Black"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #181818;\"| || 0.094, 0.094, 0.094\r\n|-\r\n|}"<br />
},<br />
"Color.DarkGrey" : {<br />
"prefix": "Color.DarkGrey",<br />
"body": [<br />
"Color.DarkGrey"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #585858;\"| || 0.345, 0.345, 0.345\r\n|-\r\n|}"<br />
},<br />
"Color.Grey" : {<br />
"prefix": "Color.Grey",<br />
"body": [<br />
"Color.Grey"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #B8B8B8;\"| || 0.722, 0.722, 0.722\r\n|-\r\n|}"<br />
},<br />
"Color.LightGrey" : {<br />
"prefix": "Color.LightGrey",<br />
"body": [<br />
"Color.LightGrey"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #D8D8D8;\"| || 0.847, 0.847, 0.847\r\n|-\r\n|}"<br />
},<br />
"Color.White" : {<br />
"prefix": "Color.White",<br />
"body": [<br />
"Color.White"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #F8F8F8;\"| || 0.972, 0.972, 0.972\r\n|-\r\n|}"<br />
},<br />
"Color.Red" : {<br />
"prefix": "Color.Red",<br />
"body": [<br />
"Color.Red"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #AB4642;\"| || 0.671, 0.275, 0.259\r\n|-\r\n|}"<br />
},<br />
"Color.Orange" : {<br />
"prefix": "Color.Orange",<br />
"body": [<br />
"Color.Orange"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #DC9656;\"| || 0.863, 0.589, 0.336\r\n|-\r\n|}"<br />
},<br />
"Color.Yellow" : {<br />
"prefix": "Color.Yellow",<br />
"body": [<br />
"Color.Yellow"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #F7CA88;\"| || 0.969, 0.792, 0.533\r\n|-\r\n|}"<br />
},<br />
"Color.Green" : {<br />
"prefix": "Color.Green",<br />
"body": [<br />
"Color.Green"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #A1B56C;\"| || 0.631, 0.71, 0.424\r\n|-\r\n|}"<br />
},<br />
"Color.Cyan" : {<br />
"prefix": "Color.Cyan",<br />
"body": [<br />
"Color.Cyan"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #86C1B9;\"| || 0.525, 0.757, 0.725\r\n|-\r\n|}"<br />
},<br />
"Color.Blue" : {<br />
"prefix": "Color.Blue",<br />
"body": [<br />
"Color.Blue"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #7CAFC2;\"| || 0.486, 0.686, 0.761\r\n|-\r\n|}"<br />
},<br />
"Color.Purple" : {<br />
"prefix": "Color.Purple",<br />
"body": [<br />
"Color.Purple"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #BA8BAF;\"| || 0.729, 0.545, 0.686\r\n|-\r\n|}"<br />
},<br />
"Color.Brown" : {<br />
"prefix": "Color.Brown",<br />
"body": [<br />
"Color.Brown"<br />
],<br />
"description": "\r\n{| class=wikitable\r\n! Preview !! RGB\r\n|-\r\n|style=\"background: #A16946;\"| || 0.631, 0.412, 0.275\r\n|-\r\n|}"<br />
},<br />
"Conversation.StartScript": {<br />
"prefix": "Conversation.StartScript",<br />
"body": [<br />
"Conversation.StartScript(${0:path}, ${1:onCompleteCallback})"<br />
],<br />
"description": "Starts a conversation from a name"<br />
},<br />
"DataPoints.GetAllDataPoints": {<br />
"prefix": "DataPoints.GetAllDataPoints",<br />
"body": [<br />
"DataPoints.GetAllDataPoints()"<br />
],<br />
"description": "Return all the data points that are currently in the level"<br />
},<br />
"DataPoints.GetObjectDataPoints": {<br />
"prefix": "DataPoints.GetObjectDataPoints",<br />
"body": [<br />
"DataPoints.GetObjectDataPoints(${0:objectName})"<br />
],<br />
"description": "Return all the data points received by an object or a character "<br />
},<br />
"DataPoints.FilterNetwork": {<br />
"prefix": "DataPoints.FilterNetwork",<br />
"body": [<br />
"DataPoints.FilterNetwork(${0:dataPoints}, ${1:networkName})"<br />
],<br />
"description": "Filter data points with the network name"<br />
},<br />
"DataPoints.FilterDataType": {<br />
"prefix": "DataPoints.FilterDataType",<br />
"body": [<br />
"DataPoints.FilterDataType(${0:dataPoints}, ${1:dataType})"<br />
],<br />
"description": "Filter data points with the data type"<br />
},<br />
"DataPoints.DeleteDataPoints": {<br />
"prefix": "DataPoints.DeleteDataPoints",<br />
"body": [<br />
"DataPoints.DeleteDataPoints(${0:dataPoints})"<br />
],<br />
"description": "Remove data points from the level"<br />
},<br />
"DataPoints.GetData": {<br />
"prefix": "DataPoints.GetData",<br />
"body": [<br />
"DataPoints.GetData(${0:dataPoints})"<br />
],<br />
"description": "Return all the data string from the given data points"<br />
},<br />
"DataPoints.GetDataPointInfo": {<br />
"prefix": "DataPoints.GetDataPointInfo",<br />
"body": [<br />
"DataPoints.GetDataPointInfo(${0:dataPoints})"<br />
],<br />
"description": "Return all the data info from the given data points"<br />
},<br />
"DataPoints.PlayersInventorySave": {<br />
"prefix": "DataPoints.PlayersInventorySave",<br />
"body": [<br />
"DataPoints.PlayersInventorySave(${0:dataFile})"<br />
],<br />
"description": "Save data info in the the players inventory"<br />
},<br />
"DataPoints.PlayersInventoryRemove": {<br />
"prefix": "DataPoints.PlayersInventoryRemove",<br />
"body": [<br />
"DataPoints.PlayersInventoryRemove(${0:dataFile})"<br />
],<br />
"description": "Save data info in the the players inventory"<br />
},<br />
"Debug.ConnectToDevLaptop": {<br />
"prefix": "Debug.ConnectToDevLaptop",<br />
"body": [<br />
"Debug.ConnectToDevLaptop()"<br />
],<br />
"description": "Open SSH connection to DevLaptop Device, if there's one in the level."<br />
},<br />
"Debug.TeleportPlayer": {<br />
"prefix": "Debug.TeleportPlayer",<br />
"body": [<br />
"Debug.TeleportPlayer(${0:spawnPointName})"<br />
],<br />
"description": "Teleport player to a SpawnPoint in level."<br />
},<br />
"Debug.ShowPlayerPath": {<br />
"prefix": "Debug.ShowPlayerPath",<br />
"body": [<br />
"Debug.ShowPlayerPath(${0:value})"<br />
],<br />
"description": "ShowPlayerPath"<br />
},<br />
"Devices.SetPower": {<br />
"prefix": "Devices.SetPower",<br />
"body": [<br />
"Devices.SetPower(${0:deviceName}, ${1:state})"<br />
],<br />
"description": "Change the powered on state of the device"<br />
},<br />
"Devices.TogglePower": {<br />
"prefix": "Devices.TogglePower",<br />
"body": [<br />
"Devices.TogglePower(${0:deviceName})"<br />
],<br />
"description": "Flip the powered on state of the device"<br />
},<br />
"Devices.GetPower": {<br />
"prefix": "Devices.GetPower",<br />
"body": [<br />
"Devices.GetPower(${0:deviceName})"<br />
],<br />
"description": "Get the powered on state of the device"<br />
},<br />
"Devices.SetActive": {<br />
"prefix": "Devices.SetActive",<br />
"body": [<br />
"Devices.SetActive(${0:deviceName}, ${1:state})"<br />
],<br />
"description": "Change the active state of the device"<br />
},<br />
"Devices.ToggleActive": {<br />
"prefix": "Devices.ToggleActive",<br />
"body": [<br />
"Devices.ToggleActive(${0:deviceName})"<br />
],<br />
"description": "Flip the active state of the device"<br />
},<br />
"Devices.GetActive": {<br />
"prefix": "Devices.GetActive",<br />
"body": [<br />
"Devices.GetActive(${0:deviceName})"<br />
],<br />
"description": "Get the active state of the device"<br />
},<br />
"Devices.RunOnce": {<br />
"prefix": "Devices.RunOnce",<br />
"body": [<br />
"Devices.RunOnce(${0:deviceName})"<br />
],<br />
"description": "Trigger a single update of the device"<br />
},<br />
"Devices.SetAmok": {<br />
"prefix": "Devices.SetAmok",<br />
"body": [<br />
"Devices.SetAmok(${0:deviceName}, ${1:state})"<br />
],<br />
"description": "Begin an 'Amok' state, cause the device to act in an unstable/broken manor"<br />
},<br />
"Devices.ToggleAmok": {<br />
"prefix": "Devices.ToggleAmok",<br />
"body": [<br />
"Devices.ToggleAmok(${0:deviceName})"<br />
],<br />
"description": "Flip the amok state"<br />
},<br />
"Devices.GetAmok": {<br />
"prefix": "Devices.GetAmok",<br />
"body": [<br />
"Devices.GetAmok(${0:deviceName})"<br />
],<br />
"description": "Get the 'Amok' state of the device"<br />
},<br />
"Devices.SetValue": {<br />
"prefix": "Devices.SetValue",<br />
"body": [<br />
"Devices.SetValue(${0:deviceName}, ${1:newValue})"<br />
],<br />
"description": "Pass a string value to the device"<br />
},<br />
"Devices.GetValue": {<br />
"prefix": "Devices.GetValue",<br />
"body": [<br />
"Devices.GetValue(${0:deviceName})"<br />
],<br />
"description": "Get the current value of the device"<br />
},<br />
"Devices.GetDataInventory": {<br />
"prefix": "Devices.GetDataInventory",<br />
"body": [<br />
"Devices.GetDataInventory(${0:deviceName}, ${1:index})"<br />
],<br />
"description": "Get the description of the data at this index in the DataInventory"<br />
},<br />
"Devices.GetDataPointFromInventory": {<br />
"prefix": "Devices.GetDataPointFromInventory",<br />
"body": [<br />
"Devices.GetDataPointFromInventory(${0:deviceName}, ${1:index})"<br />
],<br />
"description": "Get a table of useful data from the DataPoint at this index in the DataInventory"<br />
},<br />
"Devices.GetDataInventoryCount": {<br />
"prefix": "Devices.GetDataInventoryCount",<br />
"body": [<br />
"Devices.GetDataInventoryCount(${0:deviceName})"<br />
],<br />
"description": "Get the number of DataPoints in the DataInventory of this device"<br />
},<br />
"Devices.CheckPlayerAccess": {<br />
"prefix": "Devices.CheckPlayerAccess",<br />
"body": [<br />
"Devices.CheckPlayerAccess(${0:deviceName})"<br />
],<br />
"description": "Check if the PLayer has access to a Device"<br />
},<br />
"Doors.SetZoneKeys": {<br />
"prefix": "Doors.SetZoneKeys",<br />
"body": [<br />
"Doors.SetZoneKeys(${0:zoneName}, ${1:keyNames})"<br />
],<br />
"description": "Sets a key as unlocking a specific zone"<br />
},<br />
"Doors.SetNetwork": {<br />
"prefix": "Doors.SetNetwork",<br />
"body": [<br />
"Doors.SetNetwork(${0:networkName})"<br />
],<br />
"description": "Sets the name of the network for the door system to use"<br />
},<br />
"Doors.SetKeyOnDevice": {<br />
"prefix": "Doors.SetKeyOnDevice",<br />
"body": [<br />
"Doors.SetKeyOnDevice(${0:keyName}, ${1:deviceName})"<br />
],<br />
"description": "Sets a key as the current NFC file on a net device"<br />
},<br />
"Doors.AssignKeyToCharacter": {<br />
"prefix": "Doors.AssignKeyToCharacter",<br />
"body": [<br />
"Doors.AssignKeyToCharacter(${0:keyName}, ${1:character})"<br />
],<br />
"description": "Adds a key to a characters inventory and sets it as the characters current NFC data"<br />
},<br />
"Doors.Open": {<br />
"prefix": "Doors.Open",<br />
"body": [<br />
"Doors.Open(${0:doorID})"<br />
],<br />
"description": "Open the specified door."<br />
},<br />
"Doors.Close": {<br />
"prefix": "Doors.Close",<br />
"body": [<br />
"Doors.Close(${0:doorID})"<br />
],<br />
"description": "close the specified door."<br />
},<br />
"Doors.Unlock": {<br />
"prefix": "Doors.Unlock",<br />
"body": [<br />
"Doors.Unlock(${0:doorID})"<br />
],<br />
"description": "Unlock the specified door"<br />
},<br />
"Doors.Lock": {<br />
"prefix": "Doors.Lock",<br />
"body": [<br />
"Doors.Lock(${0:doorID})"<br />
],<br />
"description": "Lock the specified door"<br />
},<br />
"Flutter.SendMessageFromCharacter": {<br />
"prefix": "Flutter.SendMessageFromCharacter",<br />
"body": [<br />
"Flutter.SendMessageFromCharacter(${0:message}, ${1:internalName})"<br />
],<br />
"description": "Sends a custom FlutterMessage from NPC in current level"<br />
},<br />
"Flutter.SendRandomMessageFromCharacter": {<br />
"prefix": "Flutter.SendRandomMessageFromCharacter",<br />
"body": [<br />
"Flutter.SendRandomMessageFromCharacter(${0:internalName})"<br />
],<br />
"description": "Sends a generated Flutter message from NPC in current level"<br />
},<br />
"Flutter.SendRandomMessage": {<br />
"prefix": "Flutter.SendRandomMessage",<br />
"body": [<br />
"Flutter.SendRandomMessage()"<br />
],<br />
"description": "Sends a generated Flutter message from generated random character"<br />
},<br />
"Flutter.SetFlutterEnabled": {<br />
"prefix": "Flutter.SetFlutterEnabled",<br />
"body": [<br />
"Flutter.SetFlutterEnabled(${0:enabled})"<br />
],<br />
"description": "Set Flutter app enabled or disabled."<br />
},<br />
"GameProgress.GetValue": {<br />
"prefix": "GameProgress.GetValue",<br />
"body": [<br />
"GameProgress.GetValue(${0:key})"<br />
],<br />
"description": "Returns a value from the game progress by key, empty string if it doesn't exist"<br />
},<br />
"GameProgress.SetValue": {<br />
"prefix": "GameProgress.SetValue",<br />
"body": [<br />
"GameProgress.SetValue(${0:key}, ${1:value}, ${2:overwrite})"<br />
],<br />
"description": "Sets a value in the game progress"<br />
},<br />
"GameProgress.HasKey": {<br />
"prefix": "GameProgress.HasKey",<br />
"body": [<br />
"GameProgress.HasKey(${0:key})"<br />
],<br />
"description": "Does the specified key exist?"<br />
},<br />
"Mission.CompletedMissionSetup_Always": {<br />
"prefix": "Mission.CompletedMissionSetup_Always",<br />
"body": [<br />
"Mission.CompletedMissionSetup_Always()"<br />
],<br />
"description": "This should be called at the end of MissionSetup_Always()\r\n\t\t\t\t\t\tAfter it the game will either load last save and start, or execute MissionSetup_NoSave()"<br />
},<br />
"Mission.CompletedMissionSetup_NoSave": {<br />
"prefix": "Mission.CompletedMissionSetup_NoSave",<br />
"body": [<br />
"Mission.CompletedMissionSetup_NoSave()"<br />
],<br />
"description": "This should be called at the end of MissionSetup_NoSave()\r\n\t\t\t\tCall this AFTER all Devices have been connected to their networks. This allows DataPoints to be processed correctly."<br />
},<br />
"Mission.MissionCompleted": {<br />
"prefix": "Mission.MissionCompleted",<br />
"body": [<br />
"Mission.MissionCompleted()"<br />
],<br />
"description": "This should be called when the final objective of the mission has been completed\r\n\t\t\t\t\t\tIt will trigger the game to fade to black and return to the main menu"<br />
},<br />
"Mission.MissionFailed": {<br />
"prefix": "Mission.MissionFailed",<br />
"body": [<br />
"Mission.MissionFailed()"<br />
],<br />
"description": "This function will fail the current mission, as if Joe had been tazed (or similar)."<br />
},<br />
"Mission.SpawnCharacter": {<br />
"prefix": "Mission.SpawnCharacter",<br />
"body": [<br />
"Mission.SpawnCharacter(${0:internalName})"<br />
],<br />
"description": "Spawn a registered character in the game"<br />
},<br />
"Mission.ObjectiveIsActive": {<br />
"prefix": "Mission.ObjectiveIsActive",<br />
"body": [<br />
"Mission.ObjectiveIsActive(${0:objectiveName})"<br />
],<br />
"description": "Returns true if objective has been started but not completed yet."<br />
},<br />
"Mission.ObjectiveIsCompleted": {<br />
"prefix": "Mission.ObjectiveIsCompleted",<br />
"body": [<br />
"Mission.ObjectiveIsCompleted(${0:objectiveName})"<br />
],<br />
"description": "Returns true if objective has been completed."<br />
},<br />
"Mission.StartObjective": {<br />
"prefix": "Mission.StartObjective",<br />
"body": [<br />
"Mission.StartObjective(${0:objectiveName})"<br />
],<br />
"description": "Start the provided objective"<br />
},<br />
"Mission.CompleteObjective": {<br />
"prefix": "Mission.CompleteObjective",<br />
"body": [<br />
"Mission.CompleteObjective(${0:objectiveName})"<br />
],<br />
"description": "Complete the provided objective"<br />
},<br />
"Mission.GetCurrentObjective": {<br />
"prefix": "Mission.GetCurrentObjective",<br />
"body": [<br />
"Mission.GetCurrentObjective()"<br />
],<br />
"description": "Get the name of the current objective"<br />
},<br />
"Mission.TriggerAutoSave": {<br />
"prefix": "Mission.TriggerAutoSave",<br />
"body": [<br />
"Mission.TriggerAutoSave()"<br />
],<br />
"description": "Triggers an autosave, only if the game is current in a mission"<br />
},<br />
"Mission.SetMissionObjectInteractionRequirement": {<br />
"prefix": "Mission.SetMissionObjectInteractionRequirement",<br />
"body": [<br />
"Mission.SetMissionObjectInteractionRequirement(${0:name}, ${1:requirement})"<br />
],<br />
"description": "Sets if a MissionObject should request the player to select an item or data file before interaction"<br />
},<br />
"Mission.SetCharacterInteractionRequirement": {<br />
"prefix": "Mission.SetCharacterInteractionRequirement",<br />
"body": [<br />
"Mission.SetCharacterInteractionRequirement(${0:name}, ${1:requirement})"<br />
],<br />
"description": "Sets if a Character should request the player to select an item or data file before interaction"<br />
},<br />
"Mission.SetCharacterInteractable": {<br />
"prefix": "Mission.SetCharacterInteractable",<br />
"body": [<br />
"Mission.SetCharacterInteractable(${0:name}, ${1:state})"<br />
],<br />
"description": "Sets if a Character should be interactable or not at the moment"<br />
},<br />
"Mission.SetPlayerControlled": {<br />
"prefix": "Mission.SetPlayerControlled",<br />
"body": [<br />
"Mission.SetPlayerControlled(${0:tf})"<br />
],<br />
"description": "Set whether the player is currently in control (or not). Used for cutscenes, and other things that we haven't thought of yet."<br />
},<br />
"Mission.GetBool": {<br />
"prefix": "Mission.GetBool",<br />
"body": [<br />
"Mission.GetBool(${0:key})"<br />
],<br />
"description": "Get a boolean value of a key in mission Lua script. Returns false if key doesn't exist."<br />
},<br />
"Mission.SetBool": {<br />
"prefix": "Mission.SetBool",<br />
"body": [<br />
"Mission.SetBool(${0:key}, ${1:newBool})"<br />
],<br />
"description": "Sets a boolean value of a key in mission Lua script."<br />
},<br />
"Mission.GetString": {<br />
"prefix": "Mission.GetString",<br />
"body": [<br />
"Mission.GetString(${0:key})"<br />
],<br />
"description": "Get a string value of a key in mission Lua script. Returns empty if key doesn't exist."<br />
},<br />
"Mission.SetString": {<br />
"prefix": "Mission.SetString",<br />
"body": [<br />
"Mission.SetString(${0:key}, ${1:newString})"<br />
],<br />
"description": "Sets a string value of a key in mission Lua script."<br />
},<br />
"Mission.GetNumber": {<br />
"prefix": "Mission.GetNumber",<br />
"body": [<br />
"Mission.GetNumber(${0:key})"<br />
],<br />
"description": "Get a numeric value of a key in mission Lua script. Returns 0 if key doesn't exist."<br />
},<br />
"Mission.SetNumber": {<br />
"prefix": "Mission.SetNumber",<br />
"body": [<br />
"Mission.SetNumber(${0:key}, ${1:newValue})"<br />
],<br />
"description": "Sets a numeric value of a key in mission Lua script."<br />
},<br />
"Mission.StartVibrationEvent": {<br />
"prefix": "Mission.StartVibrationEvent",<br />
"body": [<br />
"Mission.StartVibrationEvent(${0:objectName})"<br />
],<br />
"description": "Start a mission object vibration event"<br />
},<br />
"Mission.StopVibrationEvent": {<br />
"prefix": "Mission.StopVibrationEvent",<br />
"body": [<br />
"Mission.StopVibrationEvent(${0:objectName})"<br />
],<br />
"description": "Stop a mission object vibration event"<br />
},<br />
"Mission.SpawnWanderNPCs": {<br />
"prefix": "Mission.SpawnWanderNPCs",<br />
"body": [<br />
"Mission.SpawnWanderNPCs(${0:spawnPoint}, ${1:spawnNum}, ${2:characterPrefabs}, ${3:headProps}, ${4:colorTextures}, ${5:metalTextures}, ${6:gestures})"<br />
],<br />
"description": "Spawns various Wander NPCs in a randomized way"<br />
},<br />
"Network.CreateDataPoint": {<br />
"prefix": "Network.CreateDataPoint",<br />
"body": [<br />
"Network.CreateDataPoint(${0:internalName}, ${1:dataTable}, ${2:locationObject}, ${3:characterName}, ${4:networkName})"<br />
],<br />
"description": "Create a DataPoint in a specific place, from a specific character, with pre filled in data."<br />
},<br />
"Network.ConnectToNetwork": {<br />
"prefix": "Network.ConnectToNetwork",<br />
"body": [<br />
"Network.ConnectToNetwork(${0:connector}, ${1:targetNetwork}, ${2:accessKey})"<br />
],<br />
"description": "Connect a device (or a table of devices) to a specified network"<br />
},<br />
"Network.SendData": {<br />
"prefix": "Network.SendData",<br />
"body": [<br />
"Network.SendData(${0:internalName}, ${1:dataTable}, ${2:sender}, ${3:receiver})"<br />
],<br />
"description": "Send a data file from one device to another"<br />
},<br />
"Network.SetNetDeviceNFCData": {<br />
"prefix": "Network.SetNetDeviceNFCData",<br />
"body": [<br />
"Network.SetNetDeviceNFCData(${0:internalName}, ${1:dataTable}, ${2:deviceTable})"<br />
],<br />
"description": "Sets NFC data on a mission object"<br />
},<br />
"Network.SetNetDeviceNFC": {<br />
"prefix": "Network.SetNetDeviceNFC",<br />
"body": [<br />
"Network.SetNetDeviceNFC(${0:deviceName}, ${1:enabled})"<br />
],<br />
"description": "Set if a mission object supports NFCAddNetDevice"<br />
},<br />
"Noise.Emit": {<br />
"prefix": "Noise.Emit",<br />
"body": [<br />
"Noise.Emit(${0:deviceName}, ${1:noiseTable})"<br />
],<br />
"description": "Emits a noise from deviceName with attributes in noiseTable"<br />
},<br />
"Noise.Silence": {<br />
"prefix": "Noise.Silence",<br />
"body": [<br />
"Noise.Silence(${0:deviceName})"<br />
],<br />
"description": "Silences the noise"<br />
},<br />
"Particles.Play": {<br />
"prefix": "Particles.Play",<br />
"body": [<br />
"Particles.Play(${0:deviceName}, ${1:searchChildren})"<br />
],<br />
"description": "Sets the particle system into play mode and beings emitting"<br />
},<br />
"Particles.Pause": {<br />
"prefix": "Particles.Pause",<br />
"body": [<br />
"Particles.Pause(${0:deviceName}, ${1:searchChildren})"<br />
],<br />
"description": "Pauses playing the particle system."<br />
},<br />
"Particles.Stop": {<br />
"prefix": "Particles.Stop",<br />
"body": [<br />
"Particles.Stop(${0:deviceName}, ${1:searchChildren})"<br />
],<br />
"description": "Stops playing the particle system."<br />
},<br />
"Particles.Toggle": {<br />
"prefix": "Particles.Toggle",<br />
"body": [<br />
"Particles.Toggle(${0:deviceName}, ${1:searchChildren})"<br />
],<br />
"description": "Toggles the particle system playing"<br />
},<br />
"Particles.Emit": {<br />
"prefix": "Particles.Emit",<br />
"body": [<br />
"Particles.Emit(${0:deviceName}, ${1:count}, ${2:searchChildren})"<br />
],<br />
"description": "Emit _count_ particles immediately."<br />
},<br />
"Particles.IsPlaying": {<br />
"prefix": "Particles.IsPlaying",<br />
"body": [<br />
"Particles.IsPlaying(${0:deviceName}, ${1:searchChildren})"<br />
],<br />
"description": "Is the particle system playing right now?"<br />
},<br />
"Player.AddItemToInventory": {<br />
"prefix": "Player.AddItemToInventory",<br />
"body": [<br />
"Player.AddItemToInventory(${0:itemName})"<br />
],<br />
"description": "Adds an item to the players inventory (silently, wihtout a notification. Also check Mission.SendData() )."<br />
},<br />
"Player.RemoveItemFromInventory": {<br />
"prefix": "Player.RemoveItemFromInventory",<br />
"body": [<br />
"Player.RemoveItemFromInventory(${0:itemName})"<br />
],<br />
"description": "Removes an item to the players inventory"<br />
},<br />
"Player.HasItem": {<br />
"prefix": "Player.HasItem",<br />
"body": [<br />
"Player.HasItem(${0:itemName})"<br />
],<br />
"description": "Is an item in the players inventory"<br />
},<br />
"Player.ClearInventory": {<br />
"prefix": "Player.ClearInventory",<br />
"body": [<br />
"Player.ClearInventory()"<br />
],<br />
"description": "Removes all items from the players inventory"<br />
},<br />
"Player.SetPlayerNFCData": {<br />
"prefix": "Player.SetPlayerNFCData",<br />
"body": [<br />
"Player.SetPlayerNFCData(${0:internalName}, ${1:dataTable})"<br />
],<br />
"description": "Sets the NFC data on the players phone"<br />
},<br />
"Player.AddDataFile": {<br />
"prefix": "Player.AddDataFile",<br />
"body": [<br />
"Player.AddDataFile(${0:internalName}, ${1:dataTable})"<br />
],<br />
"description": "Adds a data file directly to the players data inventory without a sender"<br />
},<br />
"Player.ClearDataInventory": {<br />
"prefix": "Player.ClearDataInventory",<br />
"body": [<br />
"Player.ClearDataInventory()"<br />
],<br />
"description": "Removes all data files from the players data inventory"<br />
},<br />
"Player.HasDataFile": {<br />
"prefix": "Player.HasDataFile",<br />
"body": [<br />
"Player.HasDataFile(${0:dataFileName})"<br />
],<br />
"description": "Does the player have a data file with this name?"<br />
},<br />
"Player.HasEncryptedFile": {<br />
"prefix": "Player.HasEncryptedFile",<br />
"body": [<br />
"Player.HasEncryptedFile(${0:dataFileName})"<br />
],<br />
"description": "Does the player have a file with the name _dataFileName_ and is it encrypted?"<br />
},<br />
"Player.HasDecryptedFile": {<br />
"prefix": "Player.HasDecryptedFile",<br />
"body": [<br />
"Player.HasDecryptedFile(${0:dataFileName})"<br />
],<br />
"description": "Does the player have a file with the name _dataFileName_ and is it unencrypted?"<br />
},<br />
"Player.GetDataString": {<br />
"prefix": "Player.GetDataString",<br />
"body": [<br />
"Player.GetDataString(${0:internalName})"<br />
],<br />
"description": "Returns the data string of a file in the players inventory, takes in the internal name of the file"<br />
},<br />
"Player.SetDataString": {<br />
"prefix": "Player.SetDataString",<br />
"body": [<br />
"Player.SetDataString(${0:internalName}, ${1:newString})"<br />
],<br />
"description": "Sets the data string of a data file in the players data inventory"<br />
},<br />
"Player.GetAllDataFileNames": {<br />
"prefix": "Player.GetAllDataFileNames",<br />
"body": [<br />
"Player.GetAllDataFileNames()"<br />
],<br />
"description": "Return internalNames of all files in the players data inventory"<br />
},<br />
"Player.GetAllDataFiles": {<br />
"prefix": "Player.GetAllDataFiles",<br />
"body": [<br />
"Player.GetAllDataFiles()"<br />
],<br />
"description": "Return a table of all files in the players data inventory"<br />
},<br />
"Player.SendData": {<br />
"prefix": "Player.SendData",<br />
"body": [<br />
"Player.SendData(${0:internalName}, ${1:receiver})"<br />
],<br />
"description": "Send a file from player's inventory to receiver"<br />
},<br />
"Player.GetDataPointMetaValue": {<br />
"prefix": "Player.GetDataPointMetaValue",<br />
"body": [<br />
"Player.GetDataPointMetaValue(${0:dataPoint})"<br />
],<br />
"description": "Returns how much new metdata player would gain from this DataPoint"<br />
},<br />
"Player.GetSocialProfileSize": {<br />
"prefix": "Player.GetSocialProfileSize",<br />
"body": [<br />
"Player.GetSocialProfileSize(${0:internalName})"<br />
],<br />
"description": "Returns the size of the background profile player has collected about a character"<br />
},<br />
"Player.SocialProfileContainsTag": {<br />
"prefix": "Player.SocialProfileContainsTag",<br />
"body": [<br />
"Player.SocialProfileContainsTag(${0:internalName}, ${1:tag})"<br />
],<br />
"description": "Returns true if social profile contains any character data with specified tag"<br />
},<br />
"Player.SocialProfileContainsTagData": {<br />
"prefix": "Player.SocialProfileContainsTagData",<br />
"body": [<br />
"Player.SocialProfileContainsTagData(${0:internalName}, ${1:tag}, ${2:data})"<br />
],<br />
"description": "Returns true if social profile contains character data matching both tag and data"<br />
},<br />
"Player.SocialProfileContainsData": {<br />
"prefix": "Player.SocialProfileContainsData",<br />
"body": [<br />
"Player.SocialProfileContainsData(${0:internalName}, ${1:data})"<br />
],<br />
"description": "Returns true if social profile contains specified character data (regardless of it's tag)"<br />
},<br />
"Player.AddSocialProfileInformation": {<br />
"prefix": "Player.AddSocialProfileInformation",<br />
"body": [<br />
"Player.AddSocialProfileInformation(${0:internalName}, ${1:tag}, ${2:data})"<br />
],<br />
"description": "Add new background data about a character to SocialInventory"<br />
},<br />
"Player.GetSocialProfileInformation": {<br />
"prefix": "Player.GetSocialProfileInformation",<br />
"body": [<br />
"Player.GetSocialProfileInformation(${0:internalName})"<br />
],<br />
"description": "Get all known background data about a character from player's SocialInventory"<br />
},<br />
"Player.GetPlayerTrackingState": {<br />
"prefix": "Player.GetPlayerTrackingState",<br />
"body": [<br />
"Player.GetPlayerTrackingState()"<br />
],<br />
"description": "Get current tracking state from Player's phone."<br />
},<br />
"Player.GetNetPointsCount": {<br />
"prefix": "Player.GetNetPointsCount",<br />
"body": [<br />
"Player.GetNetPointsCount()"<br />
],<br />
"description": "Get current amount of NetPoints the player has."<br />
},<br />
"Player.SetNetPointsCount": {<br />
"prefix": "Player.SetNetPointsCount",<br />
"body": [<br />
"Player.SetNetPointsCount(${0:count})"<br />
],<br />
"description": "Set player's NetPoints count."<br />
},<br />
"Player.AddNetPoints": {<br />
"prefix": "Player.AddNetPoints",<br />
"body": [<br />
"Player.AddNetPoints(${0:count})"<br />
],<br />
"description": "Add more NetPoints to player"<br />
},<br />
"Player.RemoveNetPoints": {<br />
"prefix": "Player.RemoveNetPoints",<br />
"body": [<br />
"Player.RemoveNetPoints(${0:count})"<br />
],<br />
"description": "Take NetPOintsa from player."<br />
},<br />
"Player.RecoverNetPoints": {<br />
"prefix": "Player.RecoverNetPoints",<br />
"body": [<br />
"Player.RecoverNetPoints(${0:count})"<br />
],<br />
"description": "Recover used NetPoitns to player."<br />
},<br />
"Player.GetName": {<br />
"prefix": "Player.GetName",<br />
"body": [<br />
"Player.GetName()"<br />
],<br />
"description": "Get the Player's internalName"<br />
},<br />
"Player.GetLightLevel": {<br />
"prefix": "Player.GetLightLevel",<br />
"body": [<br />
"Player.GetLightLevel()"<br />
],<br />
"description": "Get the light level around the player"<br />
},<br />
"Player.SetAlwaysRagdoll": {<br />
"prefix": "Player.SetAlwaysRagdoll",<br />
"body": [<br />
"Player.SetAlwaysRagdoll(${0:state})"<br />
],<br />
"description": "Player character aways ragdolls on death"<br />
},<br />
"Player.SetInvisible": {<br />
"prefix": "Player.SetInvisible",<br />
"body": [<br />
"Player.SetInvisible(${0:state})"<br />
],<br />
"description": "Player character is invisible"<br />
},<br />
"Scheduler.CallInSecsReal": {<br />
"prefix": "Scheduler.CallInSecsReal",<br />
"body": [<br />
"Scheduler.CallInSecsReal(${0:func}, ${1:timeInSecs})"<br />
],<br />
"description": "Schedule a lua function to be called after timeInSecs in real time (counting time even if the game is paused, in a menu, or in conversation etc)"<br />
},<br />
"Scheduler.CallInSecs": {<br />
"prefix": "Scheduler.CallInSecs",<br />
"body": [<br />
"Scheduler.CallInSecs(${0:func}, ${1:timeInSecs})"<br />
],<br />
"description": "Schedule a lua function to be called after timeInSecs in game time (time doens't count when the game is paused)"<br />
},<br />
"Scheduler.CallAtTime": {<br />
"prefix": "Scheduler.CallAtTime",<br />
"body": [<br />
"Scheduler.CallAtTime(${0:func}, ${1:dateTimeString})"<br />
],<br />
"description": "Schedule a lua function to be called at specific time (defined by a date/time string formatted as dd/MM/yyyy/HH:mm)"<br />
},<br />
"Sound.TriggerEvent": {<br />
"prefix": "Sound.TriggerEvent",<br />
"body": [<br />
"Sound.TriggerEvent(${0:eventName}, ${1:sourceName})"<br />
],<br />
"description": "Triggers a sound even with name ''eventName''"<br />
},<br />
"Sound.SetRTPC": {<br />
"prefix": "Sound.SetRTPC",<br />
"body": [<br />
"Sound.SetRTPC(${0:sourceName}, ${1:RTPCName}, ${2:value})"<br />
],<br />
"description": "Set Real-Time Parameter Curve for an audio event playign on this object"<br />
},<br />
"Spectrum.DeleteDataPoint": {<br />
"prefix": "Spectrum.DeleteDataPoint",<br />
"body": [<br />
"Spectrum.DeleteDataPoint(${0:dataPoint})"<br />
],<br />
"description": "Deletes the passed in data point"<br />
},<br />
"Spectrum.SaveDataPoint": {<br />
"prefix": "Spectrum.SaveDataPoint",<br />
"body": [<br />
"Spectrum.SaveDataPoint(${0:dataPoint})"<br />
],<br />
"description": "Saves currently targeted data point to the players data inventory"<br />
},<br />
"Spectrum.FilterClear": {<br />
"prefix": "Spectrum.FilterClear",<br />
"body": [<br />
"Spectrum.FilterClear()"<br />
],<br />
"description": "Clear the Filter"<br />
},<br />
"Spectrum.FilterType (int, bool)": {<br />
"prefix": "Spectrum.FilterType (int, bool)",<br />
"body": [<br />
"Spectrum.FilterType (int, bool)(${0:t}, ${1:on})"<br />
],<br />
"description": "Filter which types of Data Spectrum shows"<br />
},<br />
"Spectrum.FilterCreator": {<br />
"prefix": "Spectrum.FilterCreator",<br />
"body": [<br />
"Spectrum.FilterCreator(${0:creator})"<br />
],<br />
"description": "Shows only Data from the Creator of the specified DataPoint. Pass nil to clear"<br />
},<br />
"Spectrum.FilterNetwork": {<br />
"prefix": "Spectrum.FilterNetwork",<br />
"body": [<br />
"Spectrum.FilterNetwork(${0:network})"<br />
],<br />
"description": "Shows only Data from the Network of the specified DataPoint. Pass nil to clear"<br />
},<br />
"System.SetLanguage": {<br />
"prefix": "System.SetLanguage",<br />
"body": [<br />
"System.SetLanguage(${0:langCode})"<br />
],<br />
"description": "Sets the game language"<br />
},<br />
"System.GetCurrentInputMethod": {<br />
"prefix": "System.GetCurrentInputMethod",<br />
"body": [<br />
"System.GetCurrentInputMethod()"<br />
],<br />
"description": "Checks what kind of input method was last used"<br />
},<br />
"Timeline.Play": {<br />
"prefix": "Timeline.Play",<br />
"body": [<br />
"Timeline.Play(${0:missionObjectName})"<br />
],<br />
"description": "Starts the timeline"<br />
},<br />
"Timeline.Pause": {<br />
"prefix": "Timeline.Pause",<br />
"body": [<br />
"Timeline.Pause(${0:missionObjectName})"<br />
],<br />
"description": "Pauses the timeline"<br />
},<br />
"Timeline.Stop": {<br />
"prefix": "Timeline.Stop",<br />
"body": [<br />
"Timeline.Stop(${0:missionObjectName})"<br />
],<br />
"description": "Stops the timeline"<br />
},<br />
"Timeline.SetDynamicCameraTarget": {<br />
"prefix": "Timeline.SetDynamicCameraTarget",<br />
"body": [<br />
"Timeline.SetDynamicCameraTarget(${0:missionObjectName}, ${1:targetName})"<br />
],<br />
"description": "Sets the look at & follow target for all dynamically targeted cameras in the timeline"<br />
},<br />
"Timeline.PauseAI": {<br />
"prefix": "Timeline.PauseAI",<br />
"body": [<br />
"Timeline.PauseAI(${0:characterName})"<br />
],<br />
"description": "Pauses an AI agent."<br />
},<br />
"Timeline.UnpauseAI": {<br />
"prefix": "Timeline.UnpauseAI",<br />
"body": [<br />
"Timeline.UnpauseAI(${0:characterName})"<br />
],<br />
"description": "Unpauses an AI agent, resuming standard behaviour."<br />
},<br />
"UI.ToggleClock": {<br />
"prefix": "UI.ToggleClock",<br />
"body": [<br />
"UI.ToggleClock(${0:state})"<br />
],<br />
"description": "Toggles the clock hud element"<br />
},<br />
"UI.ToggleWeather": {<br />
"prefix": "UI.ToggleWeather",<br />
"body": [<br />
"UI.ToggleWeather(${0:state})"<br />
],<br />
"description": "Toggles the weather hud element"<br />
},<br />
"UI.SetDataViewState": {<br />
"prefix": "UI.SetDataViewState",<br />
"body": [<br />
"UI.SetDataViewState(${0:state})"<br />
],<br />
"description": "Sets the state of the data view"<br />
},<br />
"UI.OpenRemoteConnection": {<br />
"prefix": "UI.OpenRemoteConnection",<br />
"body": [<br />
"UI.OpenRemoteConnection(${0:missionObject})"<br />
],<br />
"description": "Opens SSH connection to currently targeted device"<br />
},<br />
"UI.SetRadialScanState": {<br />
"prefix": "UI.SetRadialScanState",<br />
"body": [<br />
"UI.SetRadialScanState(${0:shouldScan})"<br />
],<br />
"description": "Control if the radial menu should be scanning for targets"<br />
},<br />
"UI.ToggleUIMarkers": {<br />
"prefix": "UI.ToggleUIMarkers",<br />
"body": [<br />
"UI.ToggleUIMarkers(${0:state})"<br />
],<br />
"description": "Show/Hide UI markers for hackable and interactable objects near the player."<br />
},<br />
"UI.ShowHint": {<br />
"prefix": "UI.ShowHint",<br />
"body": [<br />
"UI.ShowHint(${0:message}, ${1:timeout})"<br />
],<br />
"description": "Displays a hint message. Multiple messages will stack on screen but don't go too crazy..."<br />
},<br />
"UI.ShowModalMessage": {<br />
"prefix": "UI.ShowModalMessage",<br />
"body": [<br />
"UI.ShowModalMessage(${0:luaMessage})"<br />
],<br />
"description": "Displays a modal window with text filed, an optional picture, confirm button & optional cancle button."<br />
},<br />
"UI.ShowPopup": {<br />
"prefix": "UI.ShowPopup",<br />
"body": [<br />
"UI.ShowPopup(${0:type}, ${1:header}, ${2:message}, ${3:timeout})"<br />
],<br />
"description": "Displays a small popup window in the centre of the screen"<br />
},<br />
"UI.ShowNotification": {<br />
"prefix": "UI.ShowNotification",<br />
"body": [<br />
"UI.ShowNotification(${0:type}, ${1:header}, ${2:message}, ${3:timeout})"<br />
],<br />
"description": "Displays a notification popup in the top right corner of the screen"<br />
},<br />
"UI.ShowPopupMenu": {<br />
"prefix": "UI.ShowPopupMenu",<br />
"body": [<br />
"UI.ShowPopupMenu(${0:table})"<br />
],<br />
"description": "Opens a popup menu with multiple options"<br />
},<br />
<br />
<br />
</syntaxhighlight><br />
[[Category:Modding]]<br />
[[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1723Creating Apps2023-11-15T15:16:37Z<p>WikiAdmin: /* App configuration */</p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
useSpectrum = true,<br />
range = 10,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
OnTrigger = function(target)<br />
-- What to do when player triggers this (active) app<br />
end,<br />
<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints using the app costs (or reserves, if Passive app)<br />
|-<br />
| appType<br />
|align="center"| optional<br />
| Is this an Active or a Passive app<br />
|-<br />
| useSpectrum<br />
| align="center"|optional<br />
| Should this app automatically turn on the Spectrum when used.<br />
|-<br />
| range<br />
| align="center"|optional<br />
| How far should the Spectrum view & targeting work when using this app<br />
|-<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppDock<br />
|align="center"| optional<br />
| How the app should be displayed in the AppDock. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| required<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== useSpectrum ===<br />
<br />
When set to true, the Spectrum (data view) will be automatically toggled on as needed when suing this app.<br />
<br />
=== range ===<br />
<br />
Thw working range for this app, controls how far away targets can be picked and the max range for the Spectrum. The value is in meters.<br />
<br />
=== appType ===<br />
<br />
Defines if the app is an active one (something the player triggers to do an action) or a passive one (something that runs in the background, and is toggled on & off instead of using it for an immediate action).<br />
This also relates to app cost, active apps will consume the app's cost in NetPoints when sued, while passive apps will instead reserve the cost in NetPoints for the time the app is running, reducing how many points the player has available for use.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppDock ===<br />
<br />
Rules for if, and how, the app should be listed in the AppDock. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
<br />
=== OnTrigger(target) ===<br />
<br />
OnTrigger function is what gets executed when the player points at a target and triggers an active app.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimum you should do is to set the app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
<br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
<br />
=== App Window ===<br />
<br />
Apps can also open a separate UI window of their own, with a background picture, optional progress bar, and few line's worth of space for displaying some text to the player.<br />
<br />
Be aware that there can only be one AppWindow at a time, any attempts to open a new one while some other app is already displaying a window will just be silently ignored.<br />
<br />
A good example of this, using a Lua coroutine to add some delays to the output, would be in the FlutterSearch app:<br />
<br />
<source lang="lua"><br />
app = {<br />
name = "FlutterSearch",<br />
description = "Search the local networks for files with matching Flutter ads cookie",<br />
targets = { TargetType.Character },<br />
range = 10.0,<br />
cost = 5,<br />
useSpectrum = true,<br />
appType = AppTypes.Active,<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
state = AppState.off,<br />
icon = "FlutterSearch.png",<br />
iconColor = Color.Cyan,<br />
<br />
OnTrigger = function(target)<br />
app.targetCharacter = target<br />
StartLuaCoroutine(app.DownloadRoutine)<br />
end,<br />
<br />
}<br />
<br />
function app:DownloadRoutine()<br />
<br />
-- Get all the target character's data from Spectrum:<br />
local dataPoints = DataPoints.GetObjectDataPoints(app.targetCharacter.name)<br />
<br />
-- filter for only points with metadata value<br />
local pointsToSave = {} <br />
if #dataPoints > 0 then<br />
for _, d in ipairs(dataPoints) do<br />
local metavalue = Player.GetDataPointMetaValue(d)<br />
if metavalue >= 1 then<br />
table.insert(pointsToSave, d)<br />
end<br />
end<br />
end<br />
<br />
Apps.Console_OutputLine(app.name, "Searching for files with Flutter ads cookie: " .. app.targetCharacter.name )<br />
<br />
<br />
if #pointsToSave > 0 then -- Found some valuable data, let's start "downloading" the files<br />
<br />
-- Open the AppWindow<br />
CreateAppWindow("FlutterSearch", "FlutterSearch_AppWindowBkg.png")<br />
ShowAppWindowProgress(true)<br />
ShowAppWindow()<br />
<br />
local progressPerPoint = 0.5/#pointsToSave<br />
local progress = 0<br />
local windowText = "Found Flutter ad profile '" .. app.targetCharacter.name .. "'"<br />
<br />
-- First some filler log messages, and a popup while "working" for next 5 seconds<br />
Apps.Console_OutputLine(app.name, windowText)<br />
SetAppWindowContent(windowText)<br />
<br />
for i = 0, 50, 1 do<br />
progress = progress + 0.1<br />
SetAppWindowProgress(progress)<br />
coroutine.yield(0.1)<br />
end<br />
<br />
windowText = windowText .. "\n" .. #pointsToSave .. " files found.</size>"<br />
SetAppWindowContent(windowText)<br />
<br />
coroutine.yield(1)<br />
<br />
-- Then "download" files one by one<br />
windowText = windowText .. "\nDownloading files..."<br />
SetAppWindowContent(windowText)<br />
for _, d in ipairs(pointsToSave) do<br />
Apps.Console_OutputLine(app.name, "cp -n //" .. app.targetCharacter.name .. "/" .. d.info.dataPointName .. " ~/Dowloads/" .. d.info.dataPointName)<br />
Spectrum.SaveDataPoint(d)<br />
progress = progress + progressPerPoint<br />
SetAppWindowProgress(progress)<br />
coroutine.yield(0.1)<br />
end<br />
<br />
windowText = windowText .. "\nCompleted, closing connection."<br />
SetAppWindowContent(windowText)<br />
coroutine.yield(1)<br />
RemoveAppWindow()<br />
<br />
else -- no data of any value, say "sorry" and refund the points:<br />
local noDataText = "Could not find any data for" .. app.targetCharacter.name<br />
Apps.Console_OutputLine(app.name, noDataText )<br />
UI.ShowPopUp(PopupType.generic, "FlutterSearch", noDataText, 3)<br />
Player.RecoverNetPoints(app.cost)<br />
end<br />
</source><br />
<br />
==== CreateAppWindow("window title", "backgroundImage.png") ====<br />
<br />
Creates the app window, and sets the window's tiutle and background. The window will not be visible at this point.<br />
<br />
==== ShowAppWindowProgress(true|false) ====<br />
<br />
Enable/disable a progress bar at the boittom of the window.<br />
<br />
==== ShowAppWindow() ====<br />
<br />
Calling this will make the app window visible to the player. Typically you'd do this after setting the window content. <br />
<br />
==== SetAppWindowContent("content text") ====<br />
<br />
Sets the content text of the app window.<br />
<br />
==== SetAppWindowProgress(0-1) ====<br />
<br />
Sets the progress bar at the botom of the window (if enabled). Use values from 0 to 1.<br />
<br />
==== RemoveAppWindow() ====<br />
<br />
Hides the app window and removes it.<br />
<br />
<br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Template:Level_Breakdown&diff=1703Template:Level Breakdown2023-11-10T13:20:22Z<p>WikiAdmin: </p>
<hr />
<div>== The Story So Far ==<br />
a BRIEF summary of what has happened to get Joe to this point<br />
<br />
== Story Beats / Script Notes ==<br />
Key story beats to be delivered in this scene (the pieces of story and dialog that MUST be present)<br />
<br />
== Tutorialisation In Level ==<br />
Any tutorialisation that needs to be considered in the level flow<br />
<br />
== Location ==<br />
An overview of the location, POIs, scale, vibe, anything important that will affect gameplay style or level flow<br />
<br />
== Player Path ==<br />
Player path - where possible detail the player path (this may be more freeform and up to the designer depending on the level)<br />
<br />
== NPCs ==<br />
Enemy NPCs, how many, where? anything unique we need to know about them or how they behave?<br />
<br />
== Critical Game Objects ==<br />
Critical Mission objects, devices, data files, data points, NPC profiles - any that are integral to the script and game flow (there will be many that are added by designers, but this is just capturing the integral requirements of the scene)<br />
<br />
== Critical Convos ==<br />
Critical Convos - anything that MUST be said to advance the story or delivery critical info<br />
<br />
== Critical Pop Ups ==<br />
Models, notifications that are story critical.<br />
<br />
== Critical Gameplay ==<br />
Vents, hacking puzzles and level objectives - everything that is integral to the script and flow (the designers may add more but we should try and capture the must haves here)<br />
<br />
== Network ==<br />
Networks - what does this scene require?<br />
<br />
== Doors ==<br />
Door Systems - what zones will be utilised?<br />
<br />
== Apps ==<br />
Apps - what are we encouraging use of in this level (make sure we are making the most of the apps)<br />
<br />
== Easter Eggs ==<br />
Secrets, easter eggs, npc cameos and achievements - what do we want to hide in the level<br />
<br />
== Wish List ==<br />
Everything else that is but a wish...if we get time.</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Template:Level_Breakdown&diff=1702Template:Level Breakdown2023-11-10T13:19:04Z<p>WikiAdmin: Created blank page</p>
<hr />
<div></div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Mission_Lua_API&diff=1674Mission Lua API2023-10-19T11:50:24Z<p>WikiAdmin: /* SetCharacterInteractable */</p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Mission =<br />
== Description ==<br />
The Mission API controls the flow of the mission, this includes starting and stopping the mission, and dealing with objectives and other mission-related information<br />
== Functions ==<br />
=== CompletedMissionSetup_Always ===<br />
<syntaxhighlight source lang="lua">Mission.CompletedMissionSetup_Always()</syntaxhighlight><br />
'''Description''': This should be called at the end of MissionSetup_Always()<br />
After it the game will either load last save and start, or execute MissionSetup_NoSave()<br />
<br />
'''Returns''': Nothing<br />
<br />
=== CompletedMissionSetup_NoSave ===<br />
<syntaxhighlight source lang="lua">Mission.CompletedMissionSetup_NoSave()</syntaxhighlight><br />
'''Description''': This should be called at the end of MissionSetup_NoSave()<br />
Call this AFTER all Devices have been connected to their networks. This allows DataPoints to be processed correctly.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== MissionCompleted ===<br />
<syntaxhighlight source lang="lua">Mission.MissionCompleted()</syntaxhighlight><br />
'''Description''': This should be called when the final objective of the mission has been completed<br />
It will trigger the game to fade to black and return to the main menu<br />
<br />
'''Returns''': Nothing<br />
<br />
=== MissionFailed ===<br />
<syntaxhighlight source lang="lua">Mission.MissionFailed()</syntaxhighlight><br />
'''Description''': This function will fail the current mission, as if Joe had been tazed (or similar).<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SpawnCharacter ===<br />
<syntaxhighlight source lang="lua">Mission.SpawnCharacter(internalName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|}<br />
'''Description''': Spawn a registered character in the game<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ObjectiveIsActive ===<br />
<syntaxhighlight source lang="lua">Mission.ObjectiveIsActive(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Returns true if objective has been started but not completed yet.<br />
<br />
'''Returns''': bool<br />
<br />
=== ObjectiveIsCompleted ===<br />
<syntaxhighlight source lang="lua">Mission.ObjectiveIsCompleted(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Returns true if objective has been completed.<br />
<br />
'''Returns''': bool<br />
<br />
=== StartObjective ===<br />
<syntaxhighlight source lang="lua">Mission.StartObjective(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Start the provided objective<br />
<br />
'''Returns''': Nothing<br />
<br />
=== CompleteObjective ===<br />
<syntaxhighlight source lang="lua">Mission.CompleteObjective(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Complete the provided objective<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetCurrentObjective ===<br />
<syntaxhighlight source lang="lua">Mission.GetCurrentObjective()</syntaxhighlight><br />
'''Description''': Get the name of the current objective<br />
<br />
'''Returns''': string<br />
<br />
=== TriggerAutoSave ===<br />
<syntaxhighlight source lang="lua">Mission.TriggerAutoSave()</syntaxhighlight><br />
'''Description''': Triggers an autosave, only if the game is current in a mission<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetMissionObjectInteractionRequirement ===<br />
<syntaxhighlight source lang="lua">Mission.SetMissionObjectInteractionRequirement(name, requirement)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| name || string<br />
|-<br />
| requirement || number<br />
|}<br />
'''Description''': Sets if a MissionObject should request the player to select an item or data file before interaction<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetNPCInteractionRequirement ===<br />
<syntaxhighlight source lang="lua">Mission.SetNPCInteractionRequirement(name, requirement)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| name || string<br />
|-<br />
| requirement || number<br />
|}<br />
'''Description''': Sets if a Character should request the player to select an item or data file before interaction<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': The character must also have OnNPCInteraction function defined in the mission script!<br />
<br />
=== SetNPCInteractable ===<br />
<syntaxhighlight source lang="lua">Mission.SetNPCInteractable(name, state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| name || string<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Sets if a Character should be interactable or not at the moment<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': The character must also have OnNPCInteraction function defined in the mission script!<br />
<br />
=== SetPlayerControlled ===<br />
<syntaxhighlight source lang="lua">Mission.SetPlayerControlled(tf)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| tf || bool<br />
|}<br />
'''Description''': Set whether the player is currently in control (or not). Used for cutscenes, and other things that we haven't thought of yet.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetBool ===<br />
<syntaxhighlight source lang="lua">Mission.GetBool(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a boolean value of a key in mission Lua script. Returns false if key doesn't exist.<br />
<br />
'''Returns''': bool<br />
<br />
=== SetBool ===<br />
<syntaxhighlight source lang="lua">Mission.SetBool(key, newBool)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newBool || bool<br />
|}<br />
'''Description''': Sets a boolean value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetString ===<br />
<syntaxhighlight source lang="lua">Mission.GetString(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a string value of a key in mission Lua script. Returns empty if key doesn't exist.<br />
<br />
'''Returns''': string<br />
<br />
=== SetString ===<br />
<syntaxhighlight source lang="lua">Mission.SetString(key, newString)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newString || string<br />
|}<br />
'''Description''': Sets a string value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetNumber ===<br />
<syntaxhighlight source lang="lua">Mission.GetNumber(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a numeric value of a key in mission Lua script. Returns 0 if key doesn't exist.<br />
<br />
'''Returns''': number<br />
<br />
=== SetNumber ===<br />
<syntaxhighlight source lang="lua">Mission.SetNumber(key, newValue)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newValue || number<br />
|}<br />
'''Description''': Sets a numeric value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== StartVibrationEvent ===<br />
<syntaxhighlight source lang="lua">Mission.StartVibrationEvent(objectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectName || string<br />
|}<br />
'''Description''': Start a mission object vibration event<br />
<br />
'''Returns''': Nothing<br />
<br />
=== StopVibrationEvent ===<br />
<syntaxhighlight source lang="lua">Mission.StopVibrationEvent(objectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectName || string<br />
|}<br />
'''Description''': Stop a mission object vibration event<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SpawnWanderNPCs ===<br />
<syntaxhighlight source lang="lua">Mission.SpawnWanderNPCs(spawnPoint, spawnNum, characterPrefabs, headProps, colorTextures, metalTextures, gestures)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| spawnPoint || Lua Type<br />
|-<br />
| spawnNum || number<br />
|-<br />
| characterPrefabs || Lua Type<br />
|-<br />
| headProps || Lua Type<br />
|-<br />
| colorTextures || Lua Type<br />
|-<br />
| metalTextures || Lua Type<br />
|-<br />
| gestures || Lua Type<br />
|}<br />
'''Description''': Spawns various Wander NPCs in a randomized way<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': All the parameters, expect the spawnNum, can be a string or a table of strings. For the head props and the the gestures the string "None" can be used to have the possibility to not have any head props and gestures, respectively, in the wander NPCs. Also for the color textures and metal textures the string "Default" can be used for the character's default textures.<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 29/08/2023 16:24<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Mission_Lua_API&diff=1673Mission Lua API2023-10-19T11:50:02Z<p>WikiAdmin: /* SetCharacterInteractionRequirement */</p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Mission =<br />
== Description ==<br />
The Mission API controls the flow of the mission, this includes starting and stopping the mission, and dealing with objectives and other mission-related information<br />
== Functions ==<br />
=== CompletedMissionSetup_Always ===<br />
<syntaxhighlight source lang="lua">Mission.CompletedMissionSetup_Always()</syntaxhighlight><br />
'''Description''': This should be called at the end of MissionSetup_Always()<br />
After it the game will either load last save and start, or execute MissionSetup_NoSave()<br />
<br />
'''Returns''': Nothing<br />
<br />
=== CompletedMissionSetup_NoSave ===<br />
<syntaxhighlight source lang="lua">Mission.CompletedMissionSetup_NoSave()</syntaxhighlight><br />
'''Description''': This should be called at the end of MissionSetup_NoSave()<br />
Call this AFTER all Devices have been connected to their networks. This allows DataPoints to be processed correctly.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== MissionCompleted ===<br />
<syntaxhighlight source lang="lua">Mission.MissionCompleted()</syntaxhighlight><br />
'''Description''': This should be called when the final objective of the mission has been completed<br />
It will trigger the game to fade to black and return to the main menu<br />
<br />
'''Returns''': Nothing<br />
<br />
=== MissionFailed ===<br />
<syntaxhighlight source lang="lua">Mission.MissionFailed()</syntaxhighlight><br />
'''Description''': This function will fail the current mission, as if Joe had been tazed (or similar).<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SpawnCharacter ===<br />
<syntaxhighlight source lang="lua">Mission.SpawnCharacter(internalName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|}<br />
'''Description''': Spawn a registered character in the game<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ObjectiveIsActive ===<br />
<syntaxhighlight source lang="lua">Mission.ObjectiveIsActive(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Returns true if objective has been started but not completed yet.<br />
<br />
'''Returns''': bool<br />
<br />
=== ObjectiveIsCompleted ===<br />
<syntaxhighlight source lang="lua">Mission.ObjectiveIsCompleted(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Returns true if objective has been completed.<br />
<br />
'''Returns''': bool<br />
<br />
=== StartObjective ===<br />
<syntaxhighlight source lang="lua">Mission.StartObjective(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Start the provided objective<br />
<br />
'''Returns''': Nothing<br />
<br />
=== CompleteObjective ===<br />
<syntaxhighlight source lang="lua">Mission.CompleteObjective(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Complete the provided objective<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetCurrentObjective ===<br />
<syntaxhighlight source lang="lua">Mission.GetCurrentObjective()</syntaxhighlight><br />
'''Description''': Get the name of the current objective<br />
<br />
'''Returns''': string<br />
<br />
=== TriggerAutoSave ===<br />
<syntaxhighlight source lang="lua">Mission.TriggerAutoSave()</syntaxhighlight><br />
'''Description''': Triggers an autosave, only if the game is current in a mission<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetMissionObjectInteractionRequirement ===<br />
<syntaxhighlight source lang="lua">Mission.SetMissionObjectInteractionRequirement(name, requirement)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| name || string<br />
|-<br />
| requirement || number<br />
|}<br />
'''Description''': Sets if a MissionObject should request the player to select an item or data file before interaction<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetNPCInteractionRequirement ===<br />
<syntaxhighlight source lang="lua">Mission.SetNPCInteractionRequirement(name, requirement)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| name || string<br />
|-<br />
| requirement || number<br />
|}<br />
'''Description''': Sets if a Character should request the player to select an item or data file before interaction<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': The character must also have OnNPCInteraction function defined in the mission script!<br />
<br />
=== SetCharacterInteractable ===<br />
<syntaxhighlight source lang="lua">Mission.SetCharacterInteractable(name, state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| name || string<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Sets if a Character should be interactable or not at the moment<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': The character must also have OnCharacterInteraction function defined in the mission script!<br />
=== SetPlayerControlled ===<br />
<syntaxhighlight source lang="lua">Mission.SetPlayerControlled(tf)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| tf || bool<br />
|}<br />
'''Description''': Set whether the player is currently in control (or not). Used for cutscenes, and other things that we haven't thought of yet.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetBool ===<br />
<syntaxhighlight source lang="lua">Mission.GetBool(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a boolean value of a key in mission Lua script. Returns false if key doesn't exist.<br />
<br />
'''Returns''': bool<br />
<br />
=== SetBool ===<br />
<syntaxhighlight source lang="lua">Mission.SetBool(key, newBool)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newBool || bool<br />
|}<br />
'''Description''': Sets a boolean value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetString ===<br />
<syntaxhighlight source lang="lua">Mission.GetString(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a string value of a key in mission Lua script. Returns empty if key doesn't exist.<br />
<br />
'''Returns''': string<br />
<br />
=== SetString ===<br />
<syntaxhighlight source lang="lua">Mission.SetString(key, newString)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newString || string<br />
|}<br />
'''Description''': Sets a string value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetNumber ===<br />
<syntaxhighlight source lang="lua">Mission.GetNumber(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a numeric value of a key in mission Lua script. Returns 0 if key doesn't exist.<br />
<br />
'''Returns''': number<br />
<br />
=== SetNumber ===<br />
<syntaxhighlight source lang="lua">Mission.SetNumber(key, newValue)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newValue || number<br />
|}<br />
'''Description''': Sets a numeric value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== StartVibrationEvent ===<br />
<syntaxhighlight source lang="lua">Mission.StartVibrationEvent(objectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectName || string<br />
|}<br />
'''Description''': Start a mission object vibration event<br />
<br />
'''Returns''': Nothing<br />
<br />
=== StopVibrationEvent ===<br />
<syntaxhighlight source lang="lua">Mission.StopVibrationEvent(objectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectName || string<br />
|}<br />
'''Description''': Stop a mission object vibration event<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SpawnWanderNPCs ===<br />
<syntaxhighlight source lang="lua">Mission.SpawnWanderNPCs(spawnPoint, spawnNum, characterPrefabs, headProps, colorTextures, metalTextures, gestures)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| spawnPoint || Lua Type<br />
|-<br />
| spawnNum || number<br />
|-<br />
| characterPrefabs || Lua Type<br />
|-<br />
| headProps || Lua Type<br />
|-<br />
| colorTextures || Lua Type<br />
|-<br />
| metalTextures || Lua Type<br />
|-<br />
| gestures || Lua Type<br />
|}<br />
'''Description''': Spawns various Wander NPCs in a randomized way<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': All the parameters, expect the spawnNum, can be a string or a table of strings. For the head props and the the gestures the string "None" can be used to have the possibility to not have any head props and gestures, respectively, in the wander NPCs. Also for the color textures and metal textures the string "Default" can be used for the character's default textures.<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 29/08/2023 16:24<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Mission_Lua_API&diff=1672Mission Lua API2023-10-19T11:49:43Z<p>WikiAdmin: /* SetCharacterInteractionRequirement */</p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Mission =<br />
== Description ==<br />
The Mission API controls the flow of the mission, this includes starting and stopping the mission, and dealing with objectives and other mission-related information<br />
== Functions ==<br />
=== CompletedMissionSetup_Always ===<br />
<syntaxhighlight source lang="lua">Mission.CompletedMissionSetup_Always()</syntaxhighlight><br />
'''Description''': This should be called at the end of MissionSetup_Always()<br />
After it the game will either load last save and start, or execute MissionSetup_NoSave()<br />
<br />
'''Returns''': Nothing<br />
<br />
=== CompletedMissionSetup_NoSave ===<br />
<syntaxhighlight source lang="lua">Mission.CompletedMissionSetup_NoSave()</syntaxhighlight><br />
'''Description''': This should be called at the end of MissionSetup_NoSave()<br />
Call this AFTER all Devices have been connected to their networks. This allows DataPoints to be processed correctly.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== MissionCompleted ===<br />
<syntaxhighlight source lang="lua">Mission.MissionCompleted()</syntaxhighlight><br />
'''Description''': This should be called when the final objective of the mission has been completed<br />
It will trigger the game to fade to black and return to the main menu<br />
<br />
'''Returns''': Nothing<br />
<br />
=== MissionFailed ===<br />
<syntaxhighlight source lang="lua">Mission.MissionFailed()</syntaxhighlight><br />
'''Description''': This function will fail the current mission, as if Joe had been tazed (or similar).<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SpawnCharacter ===<br />
<syntaxhighlight source lang="lua">Mission.SpawnCharacter(internalName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|}<br />
'''Description''': Spawn a registered character in the game<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ObjectiveIsActive ===<br />
<syntaxhighlight source lang="lua">Mission.ObjectiveIsActive(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Returns true if objective has been started but not completed yet.<br />
<br />
'''Returns''': bool<br />
<br />
=== ObjectiveIsCompleted ===<br />
<syntaxhighlight source lang="lua">Mission.ObjectiveIsCompleted(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Returns true if objective has been completed.<br />
<br />
'''Returns''': bool<br />
<br />
=== StartObjective ===<br />
<syntaxhighlight source lang="lua">Mission.StartObjective(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Start the provided objective<br />
<br />
'''Returns''': Nothing<br />
<br />
=== CompleteObjective ===<br />
<syntaxhighlight source lang="lua">Mission.CompleteObjective(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Complete the provided objective<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetCurrentObjective ===<br />
<syntaxhighlight source lang="lua">Mission.GetCurrentObjective()</syntaxhighlight><br />
'''Description''': Get the name of the current objective<br />
<br />
'''Returns''': string<br />
<br />
=== TriggerAutoSave ===<br />
<syntaxhighlight source lang="lua">Mission.TriggerAutoSave()</syntaxhighlight><br />
'''Description''': Triggers an autosave, only if the game is current in a mission<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetMissionObjectInteractionRequirement ===<br />
<syntaxhighlight source lang="lua">Mission.SetMissionObjectInteractionRequirement(name, requirement)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| name || string<br />
|-<br />
| requirement || number<br />
|}<br />
'''Description''': Sets if a MissionObject should request the player to select an item or data file before interaction<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetCharacterInteractionRequirement ===<br />
<syntaxhighlight source lang="lua">Mission.SetNPCInteractionRequirement(name, requirement)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| name || string<br />
|-<br />
| requirement || number<br />
|}<br />
'''Description''': Sets if a Character should request the player to select an item or data file before interaction<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': The character must also have OnNPCInteraction function defined in the mission script!<br />
<br />
=== SetCharacterInteractable ===<br />
<syntaxhighlight source lang="lua">Mission.SetCharacterInteractable(name, state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| name || string<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Sets if a Character should be interactable or not at the moment<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': The character must also have OnCharacterInteraction function defined in the mission script!<br />
=== SetPlayerControlled ===<br />
<syntaxhighlight source lang="lua">Mission.SetPlayerControlled(tf)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| tf || bool<br />
|}<br />
'''Description''': Set whether the player is currently in control (or not). Used for cutscenes, and other things that we haven't thought of yet.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetBool ===<br />
<syntaxhighlight source lang="lua">Mission.GetBool(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a boolean value of a key in mission Lua script. Returns false if key doesn't exist.<br />
<br />
'''Returns''': bool<br />
<br />
=== SetBool ===<br />
<syntaxhighlight source lang="lua">Mission.SetBool(key, newBool)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newBool || bool<br />
|}<br />
'''Description''': Sets a boolean value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetString ===<br />
<syntaxhighlight source lang="lua">Mission.GetString(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a string value of a key in mission Lua script. Returns empty if key doesn't exist.<br />
<br />
'''Returns''': string<br />
<br />
=== SetString ===<br />
<syntaxhighlight source lang="lua">Mission.SetString(key, newString)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newString || string<br />
|}<br />
'''Description''': Sets a string value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetNumber ===<br />
<syntaxhighlight source lang="lua">Mission.GetNumber(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a numeric value of a key in mission Lua script. Returns 0 if key doesn't exist.<br />
<br />
'''Returns''': number<br />
<br />
=== SetNumber ===<br />
<syntaxhighlight source lang="lua">Mission.SetNumber(key, newValue)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newValue || number<br />
|}<br />
'''Description''': Sets a numeric value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== StartVibrationEvent ===<br />
<syntaxhighlight source lang="lua">Mission.StartVibrationEvent(objectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectName || string<br />
|}<br />
'''Description''': Start a mission object vibration event<br />
<br />
'''Returns''': Nothing<br />
<br />
=== StopVibrationEvent ===<br />
<syntaxhighlight source lang="lua">Mission.StopVibrationEvent(objectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectName || string<br />
|}<br />
'''Description''': Stop a mission object vibration event<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SpawnWanderNPCs ===<br />
<syntaxhighlight source lang="lua">Mission.SpawnWanderNPCs(spawnPoint, spawnNum, characterPrefabs, headProps, colorTextures, metalTextures, gestures)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| spawnPoint || Lua Type<br />
|-<br />
| spawnNum || number<br />
|-<br />
| characterPrefabs || Lua Type<br />
|-<br />
| headProps || Lua Type<br />
|-<br />
| colorTextures || Lua Type<br />
|-<br />
| metalTextures || Lua Type<br />
|-<br />
| gestures || Lua Type<br />
|}<br />
'''Description''': Spawns various Wander NPCs in a randomized way<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': All the parameters, expect the spawnNum, can be a string or a table of strings. For the head props and the the gestures the string "None" can be used to have the possibility to not have any head props and gestures, respectively, in the wander NPCs. Also for the color textures and metal textures the string "Default" can be used for the character's default textures.<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 29/08/2023 16:24<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1634Creating Apps2023-06-21T14:53:47Z<p>WikiAdmin: /* SetAppWindowContent("scontent text") */</p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
useSpectrum = true,<br />
range = 10,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
OnTrigger = function(target)<br />
-- What to do when player triggers this (active) app<br />
end,<br />
<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints using the app costs (or reserves, if Passive app)<br />
|-<br />
| appType<br />
|align="center"| optional<br />
| Is this an Active or a Passive app<br />
|-<br />
| useSpectrum<br />
| align="center"|optional<br />
| Should this app automatically turn on the Spectrum when used.<br />
| -<br />
| range<br />
| align="center"|optional<br />
| How far should the Spectrum view & targeting work when using this app<br />
| -<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppDock<br />
|align="center"| optional<br />
| How the app should be displayed in the AppDock. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| required<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== useSpectrum ===<br />
<br />
When set to true, the Spectrum (data view) will be automatically toggled on as needed when suing this app.<br />
<br />
=== range ===<br />
<br />
Thw working range for this app, controls how far away targets can be picked and the max range for the Spectrum. The value is in meters.<br />
<br />
=== appType ===<br />
<br />
Defines if the app is an active one (something the player triggers to do an action) or a passive one (something that runs in the background, and is toggled on & off instead of using it for an immediate action).<br />
This also relates to app cost, active apps will consume the app's cost in NetPoints when sued, while passive apps will instead reserve the cost in NetPoints for the time the app is running, reducing how many points the player has available for use.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppDock ===<br />
<br />
Rules for if, and how, the app should be listed in the AppDock. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
<br />
=== OnTrigger(target) ===<br />
<br />
OnTrigger function is what gets executed when the player points at a target and triggers an active app.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimum you should do is to set the app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
<br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
<br />
=== App Window ===<br />
<br />
Apps can also open a separate UI window of their own, with a background picture, optional progress bar, and few line's worth of space for displaying some text to the player.<br />
<br />
Be aware that there can only be one AppWindow at a time, any attempts to open a new one while some other app is already displaying a window will just be silently ignored.<br />
<br />
A good example of this, using a Lua coroutine to add some delays to the output, would be in the FlutterSearch app:<br />
<br />
<source lang="lua"><br />
app = {<br />
name = "FlutterSearch",<br />
description = "Search the local networks for files with matching Flutter ads cookie",<br />
targets = { TargetType.Character },<br />
range = 10.0,<br />
cost = 5,<br />
useSpectrum = true,<br />
appType = AppTypes.Active,<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
state = AppState.off,<br />
icon = "FlutterSearch.png",<br />
iconColor = Color.Cyan,<br />
<br />
OnTrigger = function(target)<br />
app.targetCharacter = target<br />
StartLuaCoroutine(app.DownloadRoutine)<br />
end,<br />
<br />
}<br />
<br />
function app:DownloadRoutine()<br />
<br />
-- Get all the target character's data from Spectrum:<br />
local dataPoints = DataPoints.GetObjectDataPoints(app.targetCharacter.name)<br />
<br />
-- filter for only points with metadata value<br />
local pointsToSave = {} <br />
if #dataPoints > 0 then<br />
for _, d in ipairs(dataPoints) do<br />
local metavalue = Player.GetDataPointMetaValue(d)<br />
if metavalue >= 1 then<br />
table.insert(pointsToSave, d)<br />
end<br />
end<br />
end<br />
<br />
Apps.Console_OutputLine(app.name, "Searching for files with Flutter ads cookie: " .. app.targetCharacter.name )<br />
<br />
<br />
if #pointsToSave > 0 then -- Found some valuable data, let's start "downloading" the files<br />
<br />
-- Open the AppWindow<br />
CreateAppWindow("FlutterSearch", "FlutterSearch_AppWindowBkg.png")<br />
ShowAppWindowProgress(true)<br />
ShowAppWindow()<br />
<br />
local progressPerPoint = 0.5/#pointsToSave<br />
local progress = 0<br />
local windowText = "Found Flutter ad profile '" .. app.targetCharacter.name .. "'"<br />
<br />
-- First some filler log messages, and a popup while "working" for next 5 seconds<br />
Apps.Console_OutputLine(app.name, windowText)<br />
SetAppWindowContent(windowText)<br />
<br />
for i = 0, 50, 1 do<br />
progress = progress + 0.1<br />
SetAppWindowProgress(progress)<br />
coroutine.yield(0.1)<br />
end<br />
<br />
windowText = windowText .. "\n" .. #pointsToSave .. " files found.</size>"<br />
SetAppWindowContent(windowText)<br />
<br />
coroutine.yield(1)<br />
<br />
-- Then "download" files one by one<br />
windowText = windowText .. "\nDownloading files..."<br />
SetAppWindowContent(windowText)<br />
for _, d in ipairs(pointsToSave) do<br />
Apps.Console_OutputLine(app.name, "cp -n //" .. app.targetCharacter.name .. "/" .. d.info.dataPointName .. " ~/Dowloads/" .. d.info.dataPointName)<br />
Spectrum.SaveDataPoint(d)<br />
progress = progress + progressPerPoint<br />
SetAppWindowProgress(progress)<br />
coroutine.yield(0.1)<br />
end<br />
<br />
windowText = windowText .. "\nCompleted, closing connection."<br />
SetAppWindowContent(windowText)<br />
coroutine.yield(1)<br />
RemoveAppWindow()<br />
<br />
else -- no data of any value, say "sorry" and refund the points:<br />
local noDataText = "Could not find any data for" .. app.targetCharacter.name<br />
Apps.Console_OutputLine(app.name, noDataText )<br />
UI.ShowPopUp(PopupType.generic, "FlutterSearch", noDataText, 3)<br />
Player.RecoverNetPoints(app.cost)<br />
end<br />
</source><br />
<br />
==== CreateAppWindow("window title", "backgroundImage.png") ====<br />
<br />
Creates the app window, and sets the window's tiutle and background. The window will not be visible at this point.<br />
<br />
==== ShowAppWindowProgress(true|false) ====<br />
<br />
Enable/disable a progress bar at the boittom of the window.<br />
<br />
==== ShowAppWindow() ====<br />
<br />
Calling this will make the app window visible to the player. Typically you'd do this after setting the window content. <br />
<br />
==== SetAppWindowContent("content text") ====<br />
<br />
Sets the content text of the app window.<br />
<br />
==== SetAppWindowProgress(0-1) ====<br />
<br />
Sets the progress bar at the botom of the window (if enabled). Use values from 0 to 1.<br />
<br />
==== RemoveAppWindow() ====<br />
<br />
Hides the app window and removes it.<br />
<br />
<br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1633Creating Apps2023-06-21T14:53:27Z<p>WikiAdmin: /* Built-in functions */</p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
useSpectrum = true,<br />
range = 10,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
OnTrigger = function(target)<br />
-- What to do when player triggers this (active) app<br />
end,<br />
<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints using the app costs (or reserves, if Passive app)<br />
|-<br />
| appType<br />
|align="center"| optional<br />
| Is this an Active or a Passive app<br />
|-<br />
| useSpectrum<br />
| align="center"|optional<br />
| Should this app automatically turn on the Spectrum when used.<br />
| -<br />
| range<br />
| align="center"|optional<br />
| How far should the Spectrum view & targeting work when using this app<br />
| -<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppDock<br />
|align="center"| optional<br />
| How the app should be displayed in the AppDock. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| required<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== useSpectrum ===<br />
<br />
When set to true, the Spectrum (data view) will be automatically toggled on as needed when suing this app.<br />
<br />
=== range ===<br />
<br />
Thw working range for this app, controls how far away targets can be picked and the max range for the Spectrum. The value is in meters.<br />
<br />
=== appType ===<br />
<br />
Defines if the app is an active one (something the player triggers to do an action) or a passive one (something that runs in the background, and is toggled on & off instead of using it for an immediate action).<br />
This also relates to app cost, active apps will consume the app's cost in NetPoints when sued, while passive apps will instead reserve the cost in NetPoints for the time the app is running, reducing how many points the player has available for use.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppDock ===<br />
<br />
Rules for if, and how, the app should be listed in the AppDock. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
<br />
=== OnTrigger(target) ===<br />
<br />
OnTrigger function is what gets executed when the player points at a target and triggers an active app.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimum you should do is to set the app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
<br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
<br />
=== App Window ===<br />
<br />
Apps can also open a separate UI window of their own, with a background picture, optional progress bar, and few line's worth of space for displaying some text to the player.<br />
<br />
Be aware that there can only be one AppWindow at a time, any attempts to open a new one while some other app is already displaying a window will just be silently ignored.<br />
<br />
A good example of this, using a Lua coroutine to add some delays to the output, would be in the FlutterSearch app:<br />
<br />
<source lang="lua"><br />
app = {<br />
name = "FlutterSearch",<br />
description = "Search the local networks for files with matching Flutter ads cookie",<br />
targets = { TargetType.Character },<br />
range = 10.0,<br />
cost = 5,<br />
useSpectrum = true,<br />
appType = AppTypes.Active,<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
state = AppState.off,<br />
icon = "FlutterSearch.png",<br />
iconColor = Color.Cyan,<br />
<br />
OnTrigger = function(target)<br />
app.targetCharacter = target<br />
StartLuaCoroutine(app.DownloadRoutine)<br />
end,<br />
<br />
}<br />
<br />
function app:DownloadRoutine()<br />
<br />
-- Get all the target character's data from Spectrum:<br />
local dataPoints = DataPoints.GetObjectDataPoints(app.targetCharacter.name)<br />
<br />
-- filter for only points with metadata value<br />
local pointsToSave = {} <br />
if #dataPoints > 0 then<br />
for _, d in ipairs(dataPoints) do<br />
local metavalue = Player.GetDataPointMetaValue(d)<br />
if metavalue >= 1 then<br />
table.insert(pointsToSave, d)<br />
end<br />
end<br />
end<br />
<br />
Apps.Console_OutputLine(app.name, "Searching for files with Flutter ads cookie: " .. app.targetCharacter.name )<br />
<br />
<br />
if #pointsToSave > 0 then -- Found some valuable data, let's start "downloading" the files<br />
<br />
-- Open the AppWindow<br />
CreateAppWindow("FlutterSearch", "FlutterSearch_AppWindowBkg.png")<br />
ShowAppWindowProgress(true)<br />
ShowAppWindow()<br />
<br />
local progressPerPoint = 0.5/#pointsToSave<br />
local progress = 0<br />
local windowText = "Found Flutter ad profile '" .. app.targetCharacter.name .. "'"<br />
<br />
-- First some filler log messages, and a popup while "working" for next 5 seconds<br />
Apps.Console_OutputLine(app.name, windowText)<br />
SetAppWindowContent(windowText)<br />
<br />
for i = 0, 50, 1 do<br />
progress = progress + 0.1<br />
SetAppWindowProgress(progress)<br />
coroutine.yield(0.1)<br />
end<br />
<br />
windowText = windowText .. "\n" .. #pointsToSave .. " files found.</size>"<br />
SetAppWindowContent(windowText)<br />
<br />
coroutine.yield(1)<br />
<br />
-- Then "download" files one by one<br />
windowText = windowText .. "\nDownloading files..."<br />
SetAppWindowContent(windowText)<br />
for _, d in ipairs(pointsToSave) do<br />
Apps.Console_OutputLine(app.name, "cp -n //" .. app.targetCharacter.name .. "/" .. d.info.dataPointName .. " ~/Dowloads/" .. d.info.dataPointName)<br />
Spectrum.SaveDataPoint(d)<br />
progress = progress + progressPerPoint<br />
SetAppWindowProgress(progress)<br />
coroutine.yield(0.1)<br />
end<br />
<br />
windowText = windowText .. "\nCompleted, closing connection."<br />
SetAppWindowContent(windowText)<br />
coroutine.yield(1)<br />
RemoveAppWindow()<br />
<br />
else -- no data of any value, say "sorry" and refund the points:<br />
local noDataText = "Could not find any data for" .. app.targetCharacter.name<br />
Apps.Console_OutputLine(app.name, noDataText )<br />
UI.ShowPopUp(PopupType.generic, "FlutterSearch", noDataText, 3)<br />
Player.RecoverNetPoints(app.cost)<br />
end<br />
</source><br />
<br />
==== CreateAppWindow("window title", "backgroundImage.png") ====<br />
<br />
Creates the app window, and sets the window's tiutle and background. The window will not be visible at this point.<br />
<br />
==== ShowAppWindowProgress(true|false) ====<br />
<br />
Enable/disable a progress bar at the boittom of the window.<br />
<br />
==== ShowAppWindow() ====<br />
<br />
Calling this will make the app window visible to the player. Typically you'd do this after setting the window content. <br />
<br />
==== SetAppWindowContent("scontent text") ====<br />
<br />
Sets the content text of the app window.<br />
<br />
==== SetAppWindowProgress(0-1) ====<br />
<br />
Sets the progress bar at the botom of the window (if enabled). Use values from 0 to 1.<br />
<br />
==== RemoveAppWindow() ====<br />
<br />
Hides the app window and removes it.<br />
<br />
<br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1632Creating Apps2023-06-21T14:44:59Z<p>WikiAdmin: /* Built-in functions */</p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
useSpectrum = true,<br />
range = 10,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
OnTrigger = function(target)<br />
-- What to do when player triggers this (active) app<br />
end,<br />
<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints using the app costs (or reserves, if Passive app)<br />
|-<br />
| appType<br />
|align="center"| optional<br />
| Is this an Active or a Passive app<br />
|-<br />
| useSpectrum<br />
| align="center"|optional<br />
| Should this app automatically turn on the Spectrum when used.<br />
| -<br />
| range<br />
| align="center"|optional<br />
| How far should the Spectrum view & targeting work when using this app<br />
| -<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppDock<br />
|align="center"| optional<br />
| How the app should be displayed in the AppDock. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| required<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== useSpectrum ===<br />
<br />
When set to true, the Spectrum (data view) will be automatically toggled on as needed when suing this app.<br />
<br />
=== range ===<br />
<br />
Thw working range for this app, controls how far away targets can be picked and the max range for the Spectrum. The value is in meters.<br />
<br />
=== appType ===<br />
<br />
Defines if the app is an active one (something the player triggers to do an action) or a passive one (something that runs in the background, and is toggled on & off instead of using it for an immediate action).<br />
This also relates to app cost, active apps will consume the app's cost in NetPoints when sued, while passive apps will instead reserve the cost in NetPoints for the time the app is running, reducing how many points the player has available for use.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppDock ===<br />
<br />
Rules for if, and how, the app should be listed in the AppDock. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
<br />
=== OnTrigger(target) ===<br />
<br />
OnTrigger function is what gets executed when the player points at a target and triggers an active app.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimum you should do is to set the app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
<br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
<br />
=== App Window ===<br />
<br />
Apps can also open a separate UI window of their own, with a background picture, optional progress bar, and few line's worth of space for displaying some text to the player.<br />
<br />
A good example of this, using Lua coroutine to add some delays to the output, would be in the FlutterSearch app:<br />
<br />
<source lang="lua"><br />
app = {<br />
name = "FlutterSearch",<br />
description = "Search the local networks for files with matching Flutter ads cookie",<br />
targets = { TargetType.Character },<br />
range = 10.0,<br />
cost = 5,<br />
useSpectrum = true,<br />
appType = AppTypes.Active,<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
state = AppState.off,<br />
icon = "FlutterSearch.png",<br />
iconColor = Color.Cyan,<br />
<br />
OnTrigger = function(target)<br />
app.targetCharacter = target<br />
StartLuaCoroutine(app.DownloadRoutine)<br />
end,<br />
<br />
}<br />
<br />
function app:DownloadRoutine()<br />
<br />
-- Get all the target character's data from Spectrum:<br />
local dataPoints = DataPoints.GetObjectDataPoints(app.targetCharacter.name)<br />
<br />
-- filter for only points with metadata value<br />
local pointsToSave = {} <br />
if #dataPoints > 0 then<br />
for _, d in ipairs(dataPoints) do<br />
local metavalue = Player.GetDataPointMetaValue(d)<br />
if metavalue >= 1 then<br />
table.insert(pointsToSave, d)<br />
end<br />
end<br />
end<br />
<br />
Apps.Console_OutputLine(app.name, "Searching for files with Flutter ads cookie: " .. app.targetCharacter.name )<br />
<br />
<br />
if #pointsToSave > 0 then -- Found some valuable data, let's start "downloading" the files<br />
<br />
-- OPen the AppWindow<br />
CreateAppWindow("FlutterSearch", "FlutterSearch_AppWindowBkg.png")<br />
ShowAppWindowProgress(true)<br />
ShowAppWindow()<br />
<br />
local progressPerPoint = 0.5/#pointsToSave<br />
local progress = 0<br />
local windowText = "Found Flutter ad profile '" .. app.targetCharacter.name .. "'"<br />
<br />
-- First some filler log messages, and a popup while "working" for next 5 seconds<br />
Apps.Console_OutputLine(app.name, windowText)<br />
SetAppWindowContent(windowText)<br />
<br />
for i = 0, 50, 1 do<br />
progress = progress + 0.1<br />
SetAppWindowProgress(progress)<br />
coroutine.yield(0.1)<br />
end<br />
<br />
windowText = windowText .. "\n" .. #pointsToSave .. " files found.</size>"<br />
SetAppWindowContent(windowText)<br />
<br />
coroutine.yield(1)<br />
<br />
-- Then "download" files one by one<br />
windowText = windowText .. "\nDownloading files..."<br />
SetAppWindowContent(windowText)<br />
for _, d in ipairs(pointsToSave) do<br />
Apps.Console_OutputLine(app.name, "cp -n //" .. app.targetCharacter.name .. "/" .. d.info.dataPointName .. " ~/Dowloads/" .. d.info.dataPointName)<br />
Spectrum.SaveDataPoint(d)<br />
progress = progress + progressPerPoint<br />
SetAppWindowProgress(progress)<br />
coroutine.yield(0.1)<br />
end<br />
<br />
windowText = windowText .. "\nCompleted, closing connection."<br />
SetAppWindowContent(windowText)<br />
coroutine.yield(1)<br />
RemoveAppWindow()<br />
<br />
else -- no data of any value, say "sorry" and refund the points:<br />
local noDataText = "Could not find any data for" .. app.targetCharacter.name<br />
Apps.Console_OutputLine(app.name, noDataText )<br />
UI.ShowPopUp(PopupType.generic, "FlutterSearch", noDataText, 3)<br />
Player.RecoverNetPoints(app.cost)<br />
end<br />
</source><br />
<br />
<br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1631Creating Apps2023-06-21T14:29:01Z<p>WikiAdmin: /* App configuration */</p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
useSpectrum = true,<br />
range = 10,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
OnTrigger = function(target)<br />
-- What to do when player triggers this (active) app<br />
end,<br />
<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints using the app costs (or reserves, if Passive app)<br />
|-<br />
| appType<br />
|align="center"| optional<br />
| Is this an Active or a Passive app<br />
|-<br />
| useSpectrum<br />
| align="center"|optional<br />
| Should this app automatically turn on the Spectrum when used.<br />
| -<br />
| range<br />
| align="center"|optional<br />
| How far should the Spectrum view & targeting work when using this app<br />
| -<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppDock<br />
|align="center"| optional<br />
| How the app should be displayed in the AppDock. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| required<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== useSpectrum ===<br />
<br />
When set to true, the Spectrum (data view) will be automatically toggled on as needed when suing this app.<br />
<br />
=== range ===<br />
<br />
Thw working range for this app, controls how far away targets can be picked and the max range for the Spectrum. The value is in meters.<br />
<br />
=== appType ===<br />
<br />
Defines if the app is an active one (something the player triggers to do an action) or a passive one (something that runs in the background, and is toggled on & off instead of using it for an immediate action).<br />
This also relates to app cost, active apps will consume the app's cost in NetPoints when sued, while passive apps will instead reserve the cost in NetPoints for the time the app is running, reducing how many points the player has available for use.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppDock ===<br />
<br />
Rules for if, and how, the app should be listed in the AppDock. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
<br />
=== OnTrigger(target) ===<br />
<br />
OnTrigger function is what gets executed when the player points at a target and triggers an active app.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimum you should do is to set the app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
<br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1630Creating Apps2023-06-21T14:26:23Z<p>WikiAdmin: /* Example Lua script */</p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
useSpectrum = true,<br />
range = 10,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
OnTrigger = function(target)<br />
-- What to do when player triggers this (active) app<br />
end,<br />
<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints using the app costs (or reserves, if Passive app)<br />
|-<br />
| appType<br />
|align="center"| optional<br />
| Is this an Active or a Passive app<br />
|-<br />
| useSpectrum<br />
| align="center"|optional<br />
| Should this app automatically turn on the Spectrum when used.<br />
| -<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppDock<br />
|align="center"| optional<br />
| How the app should be displayed in the AppDock. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| required<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== appType ===<br />
<br />
Defines if the app is an active one (something the player triggers to do an action) or a passive one (something that runs in the background, and is toggled on & off instead of using it for an immediate action).<br />
This also relates to app cost, active apps will consume the app's cost in NetPoints when sued, while passive apps will instead reserve the cost in NetPoints for the time the app is running, reducing how many points the player has available for use.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppDock ===<br />
<br />
Rules for if, and how, the app should be listed in the AppDock. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
<br />
=== OnTrigger(target) ===<br />
<br />
OnTrigger function is what gets executed when the player points at a target and triggers an active app.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimum you should do is to set the app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
<br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1629Creating Apps2023-06-21T14:25:00Z<p>WikiAdmin: </p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
useSpectrum = true,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
OnTrigger = function(target)<br />
-- What to do when player triggers this (active) app<br />
end,<br />
<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints using the app costs (or reserves, if Passive app)<br />
|-<br />
| appType<br />
|align="center"| optional<br />
| Is this an Active or a Passive app<br />
|-<br />
| useSpectrum<br />
| align="center"|optional<br />
| Should this app automatically turn on the Spectrum when used.<br />
| -<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppDock<br />
|align="center"| optional<br />
| How the app should be displayed in the AppDock. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| required<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== appType ===<br />
<br />
Defines if the app is an active one (something the player triggers to do an action) or a passive one (something that runs in the background, and is toggled on & off instead of using it for an immediate action).<br />
This also relates to app cost, active apps will consume the app's cost in NetPoints when sued, while passive apps will instead reserve the cost in NetPoints for the time the app is running, reducing how many points the player has available for use.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppDock ===<br />
<br />
Rules for if, and how, the app should be listed in the AppDock. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
<br />
=== OnTrigger(target) ===<br />
<br />
OnTrigger function is what gets executed when the player points at a target and triggers an active app.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimum you should do is to set the app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
<br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1628Creating Apps2023-06-21T14:20:02Z<p>WikiAdmin: /* App configuration */</p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
useSpectrum = true,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
OnTrigger = function(target)<br />
-- What to do when player triggers this (active) app<br />
end,<br />
<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints using the app costs (or reserves, if Passive app)<br />
|-<br />
| appType<br />
|align="center"| optional<br />
| Is this an Active or a Passive app<br />
|-<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppDock<br />
|align="center"| optional<br />
| How the app should be displayed in the AppDock. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| required<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== appType ===<br />
<br />
Defines if the app is an active one (something the player triggers to do an action) or a passive one (something that runs in the background, and is toggled on & off instead of using it for an immediate action).<br />
This also relates to app cost, active apps will consume the app's cost in NetPoints when sued, while passive apps will instead reserve the cost in NetPoints for the time the app is running, reducing how many points the player has available for use.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppDock ===<br />
<br />
Rules for if, and how, the app should be listed in the AppDock. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
== Actions &amp; Options ==<br />
<br />
The Actions and Options are the main thing usual app does. As the names suggest, Actions are what the app does when player points at a target and presses the Interact button on the controller. And Options are to provide the player with a way to customize and further define what exactly the app should be doing.<br />
<br />
Both are optional, if you just want an app that runs in the background, maybe displaying some information to player using a Status window, then you don't need any actions. And, if your app only ever behaves in single way, or the behaviour is determined by something else than the player, then you don't need to add options either.<br />
<br />
Furthermore, if your actions (and/or options) are created dynamically, you might not even need to define anything there and instead just have empty tables for them in your app script, and instead fill in the tables in [[#UpdateActions()|UpdateActions()]] function instead.<br />
<br />
Here's an example from the DataUtil app, with one action that works differently based on the options selected at the moment:<br />
<br />
<source lang="lua">actions = {<br />
{<br />
name = "Action",<br />
run = function(target)<br />
if app.options.save.enabled then<br />
Spectrum.SaveDataPoint(target.data)<br />
end<br />
if app.options.delete.enabled then<br />
Spectrum.DeleteDataPoint(target.data)<br />
end<br />
end,<br />
},<br />
},<br />
<br />
options = {<br />
save = {<br />
name = "Save data",<br />
enabled = true,<br />
},<br />
delete = {<br />
name = "Delete data",<br />
enabled = false,<br />
},<br />
},</source><br />
=== Actions table ===<br />
<br />
Actions table defines what interactions the app has. The first action is always automatically used when the app is triggered in 3rd-person mode. When using the app from the RadialUI, if there's more than one action, the player gets a popup menu from which to choose the desired action.<br />
<br />
<source lang="lua">actions = {<br />
{<br />
name = "DefaultAction",<br />
run = function(target)<br />
-- In 3rd person mode executing app will always trigger first action<br />
end,<br />
},<br />
{<br />
name = "ExtraAction",<br />
cost = 2,<br />
run = function(target)<br />
-- In radial menu mode if there's more than one action, triggering the app opens a popup menu to select which action to take.<br />
end,<br />
},<br />
},</source><br />
{| class="wikitable"<br />
!| Name<br />
!align="center"| Required/Optional<br />
!| Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| The name displayed to player when showing multiple actions in RadialUI<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints are required to use this action<br />
|-<br />
| run<br />
|align="center"| required<br />
| Function that gets executed when the player triggers the action<br />
|}<br />
<br />
The run function gets passed [[#target|target information]] when the action is triggered.<br />
<br />
=== Options table ===<br />
<br />
Options table lets you create some additional toggleable settings for your app. They can be accessed from both AppWheel and the RadialUI by pressing the app options button (by default d-pad down on a controller). Each options represents a true/false value which you can then check elsewhere in your app script (in an action, for example) to change the app's behaviour.<br />
<br />
<source lang="lua">options = {<br />
someOption = {<br />
name = "Some option",<br />
enabled = true,<br />
},<br />
anotherOption = {<br />
name = "Some other option",<br />
enabled = false,<br />
OnOptionChange = function()<br />
-- This runs when the option is toggled.<br />
end<br />
},<br />
},</source><br />
{| class="wikitable"<br />
!| Name<br />
!align="center"| Required/Optional<br />
!| Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| The name displayed to player in the app options menu<br />
|-<br />
| enabled<br />
|align="center"| required<br />
| true/false. The value in your script will be sued as the starting value.<br />
|-<br />
| OnOptionChange<br />
|align="center"| required<br />
| Function that gets executed the option is toggled.<br />
|}<br />
<br />
Typical use for the OnOptionChange() function would be to change the icon in app's Status window to reflect what the app is set to do at the moment.<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimun you should do is to se tthe app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
=== UpdateActions() ===<br />
<br />
If the app has UpdateActions() function, it's called when the player has selected a target and presses a button to trigger the app. It let's you rewrite the app's [[#Actions_table|action table]] to change what the app does, or what kind of actions the player can take. The game passes the app [[#target|target information]] when calling this function.<br />
<br />
One option would be to just rewrite the whole actions table based on target type:<br />
<br />
<source lang="lua">UpdateActions = function(target)<br />
<br />
if target.type == TargetType.Hackable then<br />
app.actions = {<br />
{<br />
--<br />
},<br />
{<br />
--<br />
},<br />
}<br />
else if target.type == TargetType.Character then<br />
app.actions = {<br />
{<br />
--<br />
},<br />
}<br />
else<br />
app.actions = {}<br />
end<br />
<br />
end,</source><br />
You can also use this to dynamically create a list of actions. The following code checks if the target is either a hackable device or a character (so, a target type that you can receive data), and then uses the Player API to get a list of files in player's inventory and adds a new action each file:<br />
<br />
<source lang="lua">UpdateActions = function(target)<br />
<br />
if target.type == TargetType.Hackable or target.type == TargetType.Character then<br />
app.actions = {<br />
{<br />
name = "Select file to send:",<br />
}<br />
}<br />
playerFiles = Player.GetAllDataFiles()<br />
for _, file in ipairs(playerFiles) do<br />
newAction = {}<br />
newAction.name = file.name<br />
newAction.cost = 1<br />
newAction.run = function(target)<br />
message = string.format("Sending file %s to %s", file.name, target.name)<br />
UI.ShowPopUp(PopupType.progress, "FileShare", message, 3)<br />
Scheduler.CallInSecsReal(function()<br />
Player.SendData(file.internalName, target.name)<br />
end, 3)<br />
end<br />
table.insert(app.actions, newAction)<br />
end<br />
else<br />
app.actions = {}<br />
end<br />
<br />
end,</source><br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1627Creating Apps2023-06-21T14:19:15Z<p>WikiAdmin: /* App configuration */</p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
useSpectrum = true,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
OnTrigger = function(target)<br />
-- What to do when player triggers this (active) app<br />
end,<br />
<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints using the app costs (or reserves, if Passive app)<br />
|-<br />
| appType<br />
|align="center"| optional<br />
| Is this an Active or a Passive app<br />
|-<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppDock<br />
|align="center"| optional<br />
| How the app should be displayed in the AppDock. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| optional<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== appType ===<br />
<br />
Defines if the app is an active one (something the player triggers to do an action) or a passive one (something that runs in the background, and is toggled on & off instead of using it for an immediate action).<br />
This also relates to app cost, active apps will consume the app's cost in NetPoints when sued, while passive apps will instead reserve the cost in NetPoints for the time the app is running, reducing how many points the player has available for use.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppDock ===<br />
<br />
Rules for if, and how, the app should be listed in the AppDock. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
== Actions &amp; Options ==<br />
<br />
The Actions and Options are the main thing usual app does. As the names suggest, Actions are what the app does when player points at a target and presses the Interact button on the controller. And Options are to provide the player with a way to customize and further define what exactly the app should be doing.<br />
<br />
Both are optional, if you just want an app that runs in the background, maybe displaying some information to player using a Status window, then you don't need any actions. And, if your app only ever behaves in single way, or the behaviour is determined by something else than the player, then you don't need to add options either.<br />
<br />
Furthermore, if your actions (and/or options) are created dynamically, you might not even need to define anything there and instead just have empty tables for them in your app script, and instead fill in the tables in [[#UpdateActions()|UpdateActions()]] function instead.<br />
<br />
Here's an example from the DataUtil app, with one action that works differently based on the options selected at the moment:<br />
<br />
<source lang="lua">actions = {<br />
{<br />
name = "Action",<br />
run = function(target)<br />
if app.options.save.enabled then<br />
Spectrum.SaveDataPoint(target.data)<br />
end<br />
if app.options.delete.enabled then<br />
Spectrum.DeleteDataPoint(target.data)<br />
end<br />
end,<br />
},<br />
},<br />
<br />
options = {<br />
save = {<br />
name = "Save data",<br />
enabled = true,<br />
},<br />
delete = {<br />
name = "Delete data",<br />
enabled = false,<br />
},<br />
},</source><br />
=== Actions table ===<br />
<br />
Actions table defines what interactions the app has. The first action is always automatically used when the app is triggered in 3rd-person mode. When using the app from the RadialUI, if there's more than one action, the player gets a popup menu from which to choose the desired action.<br />
<br />
<source lang="lua">actions = {<br />
{<br />
name = "DefaultAction",<br />
run = function(target)<br />
-- In 3rd person mode executing app will always trigger first action<br />
end,<br />
},<br />
{<br />
name = "ExtraAction",<br />
cost = 2,<br />
run = function(target)<br />
-- In radial menu mode if there's more than one action, triggering the app opens a popup menu to select which action to take.<br />
end,<br />
},<br />
},</source><br />
{| class="wikitable"<br />
!| Name<br />
!align="center"| Required/Optional<br />
!| Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| The name displayed to player when showing multiple actions in RadialUI<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints are required to use this action<br />
|-<br />
| run<br />
|align="center"| required<br />
| Function that gets executed when the player triggers the action<br />
|}<br />
<br />
The run function gets passed [[#target|target information]] when the action is triggered.<br />
<br />
=== Options table ===<br />
<br />
Options table lets you create some additional toggleable settings for your app. They can be accessed from both AppWheel and the RadialUI by pressing the app options button (by default d-pad down on a controller). Each options represents a true/false value which you can then check elsewhere in your app script (in an action, for example) to change the app's behaviour.<br />
<br />
<source lang="lua">options = {<br />
someOption = {<br />
name = "Some option",<br />
enabled = true,<br />
},<br />
anotherOption = {<br />
name = "Some other option",<br />
enabled = false,<br />
OnOptionChange = function()<br />
-- This runs when the option is toggled.<br />
end<br />
},<br />
},</source><br />
{| class="wikitable"<br />
!| Name<br />
!align="center"| Required/Optional<br />
!| Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| The name displayed to player in the app options menu<br />
|-<br />
| enabled<br />
|align="center"| required<br />
| true/false. The value in your script will be sued as the starting value.<br />
|-<br />
| OnOptionChange<br />
|align="center"| required<br />
| Function that gets executed the option is toggled.<br />
|}<br />
<br />
Typical use for the OnOptionChange() function would be to change the icon in app's Status window to reflect what the app is set to do at the moment.<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimun you should do is to se tthe app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
=== UpdateActions() ===<br />
<br />
If the app has UpdateActions() function, it's called when the player has selected a target and presses a button to trigger the app. It let's you rewrite the app's [[#Actions_table|action table]] to change what the app does, or what kind of actions the player can take. The game passes the app [[#target|target information]] when calling this function.<br />
<br />
One option would be to just rewrite the whole actions table based on target type:<br />
<br />
<source lang="lua">UpdateActions = function(target)<br />
<br />
if target.type == TargetType.Hackable then<br />
app.actions = {<br />
{<br />
--<br />
},<br />
{<br />
--<br />
},<br />
}<br />
else if target.type == TargetType.Character then<br />
app.actions = {<br />
{<br />
--<br />
},<br />
}<br />
else<br />
app.actions = {}<br />
end<br />
<br />
end,</source><br />
You can also use this to dynamically create a list of actions. The following code checks if the target is either a hackable device or a character (so, a target type that you can receive data), and then uses the Player API to get a list of files in player's inventory and adds a new action each file:<br />
<br />
<source lang="lua">UpdateActions = function(target)<br />
<br />
if target.type == TargetType.Hackable or target.type == TargetType.Character then<br />
app.actions = {<br />
{<br />
name = "Select file to send:",<br />
}<br />
}<br />
playerFiles = Player.GetAllDataFiles()<br />
for _, file in ipairs(playerFiles) do<br />
newAction = {}<br />
newAction.name = file.name<br />
newAction.cost = 1<br />
newAction.run = function(target)<br />
message = string.format("Sending file %s to %s", file.name, target.name)<br />
UI.ShowPopUp(PopupType.progress, "FileShare", message, 3)<br />
Scheduler.CallInSecsReal(function()<br />
Player.SendData(file.internalName, target.name)<br />
end, 3)<br />
end<br />
table.insert(app.actions, newAction)<br />
end<br />
else<br />
app.actions = {}<br />
end<br />
<br />
end,</source><br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1626Creating Apps2023-06-21T14:18:43Z<p>WikiAdmin: /* Example Lua script */</p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
useSpectrum = true,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
OnTrigger = function(target)<br />
-- What to do when player triggers this (active) app<br />
end,<br />
<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints using the app costs (or reserves, if Passive app)<br />
|-<br />
| appType<br />
|align="center"| optional<br />
| Is this an Active or a Passive app<br />
|-<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppDock<br />
|align="center"| optional<br />
| How the app should be displayed in the AppDock. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| required<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== appType ===<br />
<br />
Defines if the app is an active one (something the player triggers to do an action) or a passive one (something that runs in the background, and is toggled on & off instead of using it for an immediate action).<br />
This also relates to app cost, active apps will consume the app's cost in NetPoints when sued, while passive apps will instead reserve the cost in NetPoints for the time the app is running, reducing how many points the player has available for use.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppDock ===<br />
<br />
Rules for if, and how, the app should be listed in the AppDock. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
== Actions &amp; Options ==<br />
<br />
The Actions and Options are the main thing usual app does. As the names suggest, Actions are what the app does when player points at a target and presses the Interact button on the controller. And Options are to provide the player with a way to customize and further define what exactly the app should be doing.<br />
<br />
Both are optional, if you just want an app that runs in the background, maybe displaying some information to player using a Status window, then you don't need any actions. And, if your app only ever behaves in single way, or the behaviour is determined by something else than the player, then you don't need to add options either.<br />
<br />
Furthermore, if your actions (and/or options) are created dynamically, you might not even need to define anything there and instead just have empty tables for them in your app script, and instead fill in the tables in [[#UpdateActions()|UpdateActions()]] function instead.<br />
<br />
Here's an example from the DataUtil app, with one action that works differently based on the options selected at the moment:<br />
<br />
<source lang="lua">actions = {<br />
{<br />
name = "Action",<br />
run = function(target)<br />
if app.options.save.enabled then<br />
Spectrum.SaveDataPoint(target.data)<br />
end<br />
if app.options.delete.enabled then<br />
Spectrum.DeleteDataPoint(target.data)<br />
end<br />
end,<br />
},<br />
},<br />
<br />
options = {<br />
save = {<br />
name = "Save data",<br />
enabled = true,<br />
},<br />
delete = {<br />
name = "Delete data",<br />
enabled = false,<br />
},<br />
},</source><br />
=== Actions table ===<br />
<br />
Actions table defines what interactions the app has. The first action is always automatically used when the app is triggered in 3rd-person mode. When using the app from the RadialUI, if there's more than one action, the player gets a popup menu from which to choose the desired action.<br />
<br />
<source lang="lua">actions = {<br />
{<br />
name = "DefaultAction",<br />
run = function(target)<br />
-- In 3rd person mode executing app will always trigger first action<br />
end,<br />
},<br />
{<br />
name = "ExtraAction",<br />
cost = 2,<br />
run = function(target)<br />
-- In radial menu mode if there's more than one action, triggering the app opens a popup menu to select which action to take.<br />
end,<br />
},<br />
},</source><br />
{| class="wikitable"<br />
!| Name<br />
!align="center"| Required/Optional<br />
!| Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| The name displayed to player when showing multiple actions in RadialUI<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints are required to use this action<br />
|-<br />
| run<br />
|align="center"| required<br />
| Function that gets executed when the player triggers the action<br />
|}<br />
<br />
The run function gets passed [[#target|target information]] when the action is triggered.<br />
<br />
=== Options table ===<br />
<br />
Options table lets you create some additional toggleable settings for your app. They can be accessed from both AppWheel and the RadialUI by pressing the app options button (by default d-pad down on a controller). Each options represents a true/false value which you can then check elsewhere in your app script (in an action, for example) to change the app's behaviour.<br />
<br />
<source lang="lua">options = {<br />
someOption = {<br />
name = "Some option",<br />
enabled = true,<br />
},<br />
anotherOption = {<br />
name = "Some other option",<br />
enabled = false,<br />
OnOptionChange = function()<br />
-- This runs when the option is toggled.<br />
end<br />
},<br />
},</source><br />
{| class="wikitable"<br />
!| Name<br />
!align="center"| Required/Optional<br />
!| Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| The name displayed to player in the app options menu<br />
|-<br />
| enabled<br />
|align="center"| required<br />
| true/false. The value in your script will be sued as the starting value.<br />
|-<br />
| OnOptionChange<br />
|align="center"| required<br />
| Function that gets executed the option is toggled.<br />
|}<br />
<br />
Typical use for the OnOptionChange() function would be to change the icon in app's Status window to reflect what the app is set to do at the moment.<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimun you should do is to se tthe app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
=== UpdateActions() ===<br />
<br />
If the app has UpdateActions() function, it's called when the player has selected a target and presses a button to trigger the app. It let's you rewrite the app's [[#Actions_table|action table]] to change what the app does, or what kind of actions the player can take. The game passes the app [[#target|target information]] when calling this function.<br />
<br />
One option would be to just rewrite the whole actions table based on target type:<br />
<br />
<source lang="lua">UpdateActions = function(target)<br />
<br />
if target.type == TargetType.Hackable then<br />
app.actions = {<br />
{<br />
--<br />
},<br />
{<br />
--<br />
},<br />
}<br />
else if target.type == TargetType.Character then<br />
app.actions = {<br />
{<br />
--<br />
},<br />
}<br />
else<br />
app.actions = {}<br />
end<br />
<br />
end,</source><br />
You can also use this to dynamically create a list of actions. The following code checks if the target is either a hackable device or a character (so, a target type that you can receive data), and then uses the Player API to get a list of files in player's inventory and adds a new action each file:<br />
<br />
<source lang="lua">UpdateActions = function(target)<br />
<br />
if target.type == TargetType.Hackable or target.type == TargetType.Character then<br />
app.actions = {<br />
{<br />
name = "Select file to send:",<br />
}<br />
}<br />
playerFiles = Player.GetAllDataFiles()<br />
for _, file in ipairs(playerFiles) do<br />
newAction = {}<br />
newAction.name = file.name<br />
newAction.cost = 1<br />
newAction.run = function(target)<br />
message = string.format("Sending file %s to %s", file.name, target.name)<br />
UI.ShowPopUp(PopupType.progress, "FileShare", message, 3)<br />
Scheduler.CallInSecsReal(function()<br />
Player.SendData(file.internalName, target.name)<br />
end, 3)<br />
end<br />
table.insert(app.actions, newAction)<br />
end<br />
else<br />
app.actions = {}<br />
end<br />
<br />
end,</source><br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1625Creating Apps2023-06-15T11:28:04Z<p>WikiAdmin: /* App configuration */</p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
state = AppState.off,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
actions = {<br />
{<br />
name = "DefaultAction",<br />
run = function(target)<br />
-- In 3rd person mode executing app will always trigger first action<br />
end,<br />
},<br />
{<br />
name = "ExtraAction",<br />
cost = 2.0,<br />
run = function(target)<br />
-- In radial menu mode if there's more than one action, triggering the app opens a popup menu to select which action to take.<br />
end,<br />
},<br />
},<br />
<br />
options = {<br />
someOption = {<br />
name = "Some option",<br />
enabled = true,<br />
},<br />
anotherOption = {<br />
name = "Some other option",<br />
enabled = false,<br />
OnOptionChange = function()<br />
-- This runs when the option is toggled.<br />
end<br />
},<br />
},<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
UpdateActions = function(target)<br />
-- If you want to change available actions based on the target player has selected, do it here.<br />
end,<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints using the app costs (or reserves, if Passive app)<br />
|-<br />
| appType<br />
|align="center"| optional<br />
| Is this an Active or a Passive app<br />
|-<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppDock<br />
|align="center"| optional<br />
| How the app should be displayed in the AppDock. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| required<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== appType ===<br />
<br />
Defines if the app is an active one (something the player triggers to do an action) or a passive one (something that runs in the background, and is toggled on & off instead of using it for an immediate action).<br />
This also relates to app cost, active apps will consume the app's cost in NetPoints when sued, while passive apps will instead reserve the cost in NetPoints for the time the app is running, reducing how many points the player has available for use.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppDock ===<br />
<br />
Rules for if, and how, the app should be listed in the AppDock. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
== Actions &amp; Options ==<br />
<br />
The Actions and Options are the main thing usual app does. As the names suggest, Actions are what the app does when player points at a target and presses the Interact button on the controller. And Options are to provide the player with a way to customize and further define what exactly the app should be doing.<br />
<br />
Both are optional, if you just want an app that runs in the background, maybe displaying some information to player using a Status window, then you don't need any actions. And, if your app only ever behaves in single way, or the behaviour is determined by something else than the player, then you don't need to add options either.<br />
<br />
Furthermore, if your actions (and/or options) are created dynamically, you might not even need to define anything there and instead just have empty tables for them in your app script, and instead fill in the tables in [[#UpdateActions()|UpdateActions()]] function instead.<br />
<br />
Here's an example from the DataUtil app, with one action that works differently based on the options selected at the moment:<br />
<br />
<source lang="lua">actions = {<br />
{<br />
name = "Action",<br />
run = function(target)<br />
if app.options.save.enabled then<br />
Spectrum.SaveDataPoint(target.data)<br />
end<br />
if app.options.delete.enabled then<br />
Spectrum.DeleteDataPoint(target.data)<br />
end<br />
end,<br />
},<br />
},<br />
<br />
options = {<br />
save = {<br />
name = "Save data",<br />
enabled = true,<br />
},<br />
delete = {<br />
name = "Delete data",<br />
enabled = false,<br />
},<br />
},</source><br />
=== Actions table ===<br />
<br />
Actions table defines what interactions the app has. The first action is always automatically used when the app is triggered in 3rd-person mode. When using the app from the RadialUI, if there's more than one action, the player gets a popup menu from which to choose the desired action.<br />
<br />
<source lang="lua">actions = {<br />
{<br />
name = "DefaultAction",<br />
run = function(target)<br />
-- In 3rd person mode executing app will always trigger first action<br />
end,<br />
},<br />
{<br />
name = "ExtraAction",<br />
cost = 2,<br />
run = function(target)<br />
-- In radial menu mode if there's more than one action, triggering the app opens a popup menu to select which action to take.<br />
end,<br />
},<br />
},</source><br />
{| class="wikitable"<br />
!| Name<br />
!align="center"| Required/Optional<br />
!| Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| The name displayed to player when showing multiple actions in RadialUI<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints are required to use this action<br />
|-<br />
| run<br />
|align="center"| required<br />
| Function that gets executed when the player triggers the action<br />
|}<br />
<br />
The run function gets passed [[#target|target information]] when the action is triggered.<br />
<br />
=== Options table ===<br />
<br />
Options table lets you create some additional toggleable settings for your app. They can be accessed from both AppWheel and the RadialUI by pressing the app options button (by default d-pad down on a controller). Each options represents a true/false value which you can then check elsewhere in your app script (in an action, for example) to change the app's behaviour.<br />
<br />
<source lang="lua">options = {<br />
someOption = {<br />
name = "Some option",<br />
enabled = true,<br />
},<br />
anotherOption = {<br />
name = "Some other option",<br />
enabled = false,<br />
OnOptionChange = function()<br />
-- This runs when the option is toggled.<br />
end<br />
},<br />
},</source><br />
{| class="wikitable"<br />
!| Name<br />
!align="center"| Required/Optional<br />
!| Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| The name displayed to player in the app options menu<br />
|-<br />
| enabled<br />
|align="center"| required<br />
| true/false. The value in your script will be sued as the starting value.<br />
|-<br />
| OnOptionChange<br />
|align="center"| required<br />
| Function that gets executed the option is toggled.<br />
|}<br />
<br />
Typical use for the OnOptionChange() function would be to change the icon in app's Status window to reflect what the app is set to do at the moment.<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimun you should do is to se tthe app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
=== UpdateActions() ===<br />
<br />
If the app has UpdateActions() function, it's called when the player has selected a target and presses a button to trigger the app. It let's you rewrite the app's [[#Actions_table|action table]] to change what the app does, or what kind of actions the player can take. The game passes the app [[#target|target information]] when calling this function.<br />
<br />
One option would be to just rewrite the whole actions table based on target type:<br />
<br />
<source lang="lua">UpdateActions = function(target)<br />
<br />
if target.type == TargetType.Hackable then<br />
app.actions = {<br />
{<br />
--<br />
},<br />
{<br />
--<br />
},<br />
}<br />
else if target.type == TargetType.Character then<br />
app.actions = {<br />
{<br />
--<br />
},<br />
}<br />
else<br />
app.actions = {}<br />
end<br />
<br />
end,</source><br />
You can also use this to dynamically create a list of actions. The following code checks if the target is either a hackable device or a character (so, a target type that you can receive data), and then uses the Player API to get a list of files in player's inventory and adds a new action each file:<br />
<br />
<source lang="lua">UpdateActions = function(target)<br />
<br />
if target.type == TargetType.Hackable or target.type == TargetType.Character then<br />
app.actions = {<br />
{<br />
name = "Select file to send:",<br />
}<br />
}<br />
playerFiles = Player.GetAllDataFiles()<br />
for _, file in ipairs(playerFiles) do<br />
newAction = {}<br />
newAction.name = file.name<br />
newAction.cost = 1<br />
newAction.run = function(target)<br />
message = string.format("Sending file %s to %s", file.name, target.name)<br />
UI.ShowPopUp(PopupType.progress, "FileShare", message, 3)<br />
Scheduler.CallInSecsReal(function()<br />
Player.SendData(file.internalName, target.name)<br />
end, 3)<br />
end<br />
table.insert(app.actions, newAction)<br />
end<br />
else<br />
app.actions = {}<br />
end<br />
<br />
end,</source><br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Creating_Apps&diff=1624Creating Apps2023-06-15T11:23:30Z<p>WikiAdmin: /* Example Lua script */</p>
<hr />
<div>== Introduction ==<br />
<br />
The main tool the player has in Off Grid (apart from clever mind and smooth stealth skills, of course) is the apps running on the player's phone, allowing both viewing and interacting with all the devices and data in the levels.<br />
<br />
The app system is built to allow creating all kinds of new tools, both very generic and powerful ones, but also small quality-of-life tools that just do some small very specific task, but make it more convenient for the player.<br />
<br />
It can also handle different types of targets, display information in the UI, and do pretty much anything our Lua API allows.<br />
<br />
On the game side, we handle the controls, app inventory and other boring common tasks automatically, and also keep track of the app use costs, and if the player is able to use an app at the moment or not, so what's left to the modder is just the specific way how ''your'' app should work.<br />
<br />
This page should explain what goes into an app's Lua script, but for more ideas and examples it's worth looking at the existing apps that are included in the game. You'll find them in ''StreamingAssets/Common/Apps'' folder.<br />
<br />
== Example Lua script ==<br />
<br />
<source lang="lua">app = {<br />
name = "Example App",<br />
description = "How to make your first app for Off Grid.",<br />
cost = 2,<br />
appType = AppTypes.Active,<br />
targets = {TargetType.Data},<br />
showInRadial = AppMenuState.target,<br />
showInAppDock = AppMenuState.byDefault,<br />
state = AppState.off,<br />
icon = "ExampleApp.png",<br />
iconColor = Color.Blue,<br />
statusIcons = { "option1.png", "option2.png"},<br />
<br />
actions = {<br />
{<br />
name = "DefaultAction",<br />
run = function(target)<br />
-- In 3rd person mode executing app will always trigger first action<br />
end,<br />
},<br />
{<br />
name = "ExtraAction",<br />
cost = 2.0,<br />
run = function(target)<br />
-- In radial menu mode if there's more than one action, triggering the app opens a popup menu to select which action to take.<br />
end,<br />
},<br />
},<br />
<br />
options = {<br />
someOption = {<br />
name = "Some option",<br />
enabled = true,<br />
},<br />
anotherOption = {<br />
name = "Some other option",<br />
enabled = false,<br />
OnOptionChange = function()<br />
-- This runs when the option is toggled.<br />
end<br />
},<br />
},<br />
<br />
OnStateChange = function(state)<br />
-- This runs when something on game side asks the app to change it's state<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
UpdateActions = function(target)<br />
-- If you want to change available actions based on the target player has selected, do it here.<br />
end,<br />
<br />
Update = function(time)<br />
-- Update will run continuously.<br />
end,<br />
<br />
}</source><br />
<br />
== App configuration ==<br />
<br />
The first section of the app script is used to define some default settings and basic information about what the app is and how it works.<br />
<br />
{| class="wikitable"<br />
! Name<br />
! Required/Optional<br />
! Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| Name of the app<br />
|-<br />
| description<br />
|align="center"| required<br />
| Short description of what the app does<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many seconds the tracking progress increases when using this app<br />
|-<br />
| targets<br />
|align="center"| optional<br />
| List of what [[#Target_types|target types]] the app can have<br />
|-<br />
| showInRadial<br />
|align="center"| optional<br />
| How the app should be displayed in Radial menu. See [[#Menu_options|menu options]].<br />
|-<br />
| showInAppWheel<br />
|align="center"| optional<br />
| How the app should be displayed in the AppWheel. See [[#Menu_options|menu options]].<br />
|-<br />
| state<br />
|align="center"| required<br />
| Current [[#App_states|state]] of the app.<br />
|-<br />
| icon<br />
|align="center"| required<br />
| Path to icon image<br />
|-<br />
| iconColor<br />
|align="center"| optional<br />
| background color for the app icon<br />
|-<br />
| statusIcons<br />
|align="center"| optional<br />
| List of icon images to use for [[#Status_window|status window]]<br />
|}<br />
<br />
=== name ===<br />
<br />
This is both the human-readable name of the app that will be displayed to the player in the UI and in menus, and also what's used to refer to the app in all your Lua scripts, inventories, and in game saves.<br />
<br />
=== description ===<br />
<br />
Longer description of what the app is and what it does.<br />
<br />
=== cost ===<br />
<br />
The cost (NetPoints) of having the app running. This is only needed if your app uses the [[#Update()|Update()]] function, and you actually want it to cost something. Turning on an app that has a cost will require at least that amount of available NetPoints, and then reserves them while the app is enabled.<br />
<br />
=== targets ===<br />
<br />
Targets should include either an individual [[#Target_types|target type]], or a list of target types. If you don't define anything, the game will use ''TargetTypes.None'' by default, allowing the app to work when no target is selected. For sake of clarity, it's recommended to define that in the app script anyway.<br />
<br />
<source lang="lua">targets = TargetType.Data,</source><br />
<source lang="lua">targets = {TargetType.Hackable, TargetType.Character},</source><br />
<br />
Note that TargetType.None still only lets you trigger actions from RadialUI. The AppWheel is intended for more fast actions without interrupting gameplay, and your untargeted action should be triggered by turning the app on. This make sure there's no conflict between physical interactions player does and whatever app happens to be selected at the moment in the AppWheel.<br />
<br />
=== showInRadial ===<br />
<br />
Rules for if, and how, the app should be listed in the radial UI. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== showInAppWheel ===<br />
<br />
Rules for if, and how, the app should be listed in the AppWheel. See [[#Menu_options|Menu options]] for the details.<br />
<br />
=== state ===<br />
<br />
The default starting [[#App_states|state]] of the app. This also used to store the apps ''current'' state when the game is running. Most of the time setting this to ''AppState.off'' would work fine, meaning the app is available to the player an listed in menus as defined by the showInRadial and showInAppWheel settings, but isn't doing anything at the moment.<br />
<br />
If you want the player to start your mission without this app, and then give it to the player later on (maybe from a device player hacks, or sent by another character during a conversation or something) you can set the state in the app script to ''AppState.unavailable'', and then later on call <code>Apps.RequestAppState(appName, AppState.off)</code> from the Apps API to make thew app available to the player.<br />
<br />
Here's an example onClick() function from one of our devices. This would make the app called &quot;MultiTool&quot; available to player (with a bit of delay to make things happen ''after'' player has closed the device UI and the game continues running again), and also displaying a notification to make it look like a new app was downloaded on the player character's phone:<br />
<br />
<source lang="lua">onClick = function()<br />
Scheduler.CallInSecs(function()<br />
Apps.RequestAppState("MultiTool", AppState.off)<br />
UI.ShowNotification(NotificationType.download, "System", "Installed new application: <b>MultiTool</b>", 10)<br />
end, 1)<br />
end,</source><br />
=== icon ===<br />
<br />
Name (and path) to the icon file used for the app in the game. The icon should be a square PNG image, with transparent background and a bit of empty space on the sides so it'll fit nicely to the AppWheel &amp; Radial UI. (The borders and backgrounds seen in-game are part of the UI, so the icon really is just the &quot;logo&quot; of the app).<br />
<br />
=== iconColor ===<br />
<br />
The background color for the app icon (used in AppWheel, Radial UI and the Apps menu). The color you set here will be what's sued when the app is switched on, while the off/disabled etc colors are calculated automatically. Any alpha value in color is ignored.<br />
<br />
You can either defince RGB values, or use one of our preset colors:<br />
<br />
<source lang="lua">iconColor = Color.Blue,</source><br />
<source lang="lua">iconColor = {0.2, 0.8, 0.6, 1.0},</source><br />
=== statusIcons ===<br />
<br />
If you want your app to display a [[#Status_window|status window]], you can use this to define one or more icons to be used with it. Same rules as with the app icon so PNG, transparent background, and some space left on sides.<br />
<br />
You can add colors to your icon images if you want to, but for best fit with the game and to be able to [[#SetStatusIconColor()|set the icon color through code]] (rather than having to load a separate image file for different colors) making the icon white works the best.<br />
<br />
== Actions &amp; Options ==<br />
<br />
The Actions and Options are the main thing usual app does. As the names suggest, Actions are what the app does when player points at a target and presses the Interact button on the controller. And Options are to provide the player with a way to customize and further define what exactly the app should be doing.<br />
<br />
Both are optional, if you just want an app that runs in the background, maybe displaying some information to player using a Status window, then you don't need any actions. And, if your app only ever behaves in single way, or the behaviour is determined by something else than the player, then you don't need to add options either.<br />
<br />
Furthermore, if your actions (and/or options) are created dynamically, you might not even need to define anything there and instead just have empty tables for them in your app script, and instead fill in the tables in [[#UpdateActions()|UpdateActions()]] function instead.<br />
<br />
Here's an example from the DataUtil app, with one action that works differently based on the options selected at the moment:<br />
<br />
<source lang="lua">actions = {<br />
{<br />
name = "Action",<br />
run = function(target)<br />
if app.options.save.enabled then<br />
Spectrum.SaveDataPoint(target.data)<br />
end<br />
if app.options.delete.enabled then<br />
Spectrum.DeleteDataPoint(target.data)<br />
end<br />
end,<br />
},<br />
},<br />
<br />
options = {<br />
save = {<br />
name = "Save data",<br />
enabled = true,<br />
},<br />
delete = {<br />
name = "Delete data",<br />
enabled = false,<br />
},<br />
},</source><br />
=== Actions table ===<br />
<br />
Actions table defines what interactions the app has. The first action is always automatically used when the app is triggered in 3rd-person mode. When using the app from the RadialUI, if there's more than one action, the player gets a popup menu from which to choose the desired action.<br />
<br />
<source lang="lua">actions = {<br />
{<br />
name = "DefaultAction",<br />
run = function(target)<br />
-- In 3rd person mode executing app will always trigger first action<br />
end,<br />
},<br />
{<br />
name = "ExtraAction",<br />
cost = 2,<br />
run = function(target)<br />
-- In radial menu mode if there's more than one action, triggering the app opens a popup menu to select which action to take.<br />
end,<br />
},<br />
},</source><br />
{| class="wikitable"<br />
!| Name<br />
!align="center"| Required/Optional<br />
!| Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| The name displayed to player when showing multiple actions in RadialUI<br />
|-<br />
| cost<br />
|align="center"| optional<br />
| How many NetPoints are required to use this action<br />
|-<br />
| run<br />
|align="center"| required<br />
| Function that gets executed when the player triggers the action<br />
|}<br />
<br />
The run function gets passed [[#target|target information]] when the action is triggered.<br />
<br />
=== Options table ===<br />
<br />
Options table lets you create some additional toggleable settings for your app. They can be accessed from both AppWheel and the RadialUI by pressing the app options button (by default d-pad down on a controller). Each options represents a true/false value which you can then check elsewhere in your app script (in an action, for example) to change the app's behaviour.<br />
<br />
<source lang="lua">options = {<br />
someOption = {<br />
name = "Some option",<br />
enabled = true,<br />
},<br />
anotherOption = {<br />
name = "Some other option",<br />
enabled = false,<br />
OnOptionChange = function()<br />
-- This runs when the option is toggled.<br />
end<br />
},<br />
},</source><br />
{| class="wikitable"<br />
!| Name<br />
!align="center"| Required/Optional<br />
!| Description<br />
|-<br />
| name<br />
|align="center"| required<br />
| The name displayed to player in the app options menu<br />
|-<br />
| enabled<br />
|align="center"| required<br />
| true/false. The value in your script will be sued as the starting value.<br />
|-<br />
| OnOptionChange<br />
|align="center"| required<br />
| Function that gets executed the option is toggled.<br />
|}<br />
<br />
Typical use for the OnOptionChange() function would be to change the icon in app's Status window to reflect what the app is set to do at the moment.<br />
<br />
== Functions ==<br />
<br />
There are a few functions that the game tries to call in different situations, so depending on the app you are making you might want to include some of these in your script.<br />
<br />
=== OnStateChange() ===<br />
<br />
OnStateChange function handles the game requesting the app to change it's state, to turn the app on, or off, and so on, either because the player pressed a button to do so, or some other Lua script or game system asked for it. Ultimately, the app itself gets to decide how to handle the request, although usually you would want the app to comply. When the game calls this function, it will pass the requested [[#App_states|app state]] to the function.<br />
<br />
The minimun you should do is to se tthe app's state to the requested one, and then trigger the built-in API function to tell the game to change state there as well:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
If the app script doesn't include OnStateChange() function, the game assumes this is all you want to do and as a fallback solution handles it for you automatically.<br />
<br />
You can also add additional things the app should do when changing the state, for example to show or hide a [[#Status_window|status window]] as the app is toggled on and off:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,</source><br />
=== UpdateActions() ===<br />
<br />
If the app has UpdateActions() function, it's called when the player has selected a target and presses a button to trigger the app. It let's you rewrite the app's [[#Actions_table|action table]] to change what the app does, or what kind of actions the player can take. The game passes the app [[#target|target information]] when calling this function.<br />
<br />
One option would be to just rewrite the whole actions table based on target type:<br />
<br />
<source lang="lua">UpdateActions = function(target)<br />
<br />
if target.type == TargetType.Hackable then<br />
app.actions = {<br />
{<br />
--<br />
},<br />
{<br />
--<br />
},<br />
}<br />
else if target.type == TargetType.Character then<br />
app.actions = {<br />
{<br />
--<br />
},<br />
}<br />
else<br />
app.actions = {}<br />
end<br />
<br />
end,</source><br />
You can also use this to dynamically create a list of actions. The following code checks if the target is either a hackable device or a character (so, a target type that you can receive data), and then uses the Player API to get a list of files in player's inventory and adds a new action each file:<br />
<br />
<source lang="lua">UpdateActions = function(target)<br />
<br />
if target.type == TargetType.Hackable or target.type == TargetType.Character then<br />
app.actions = {<br />
{<br />
name = "Select file to send:",<br />
}<br />
}<br />
playerFiles = Player.GetAllDataFiles()<br />
for _, file in ipairs(playerFiles) do<br />
newAction = {}<br />
newAction.name = file.name<br />
newAction.cost = 1<br />
newAction.run = function(target)<br />
message = string.format("Sending file %s to %s", file.name, target.name)<br />
UI.ShowPopUp(PopupType.progress, "FileShare", message, 3)<br />
Scheduler.CallInSecsReal(function()<br />
Player.SendData(file.internalName, target.name)<br />
end, 3)<br />
end<br />
table.insert(app.actions, newAction)<br />
end<br />
else<br />
app.actions = {}<br />
end<br />
<br />
end,</source><br />
=== Update() ===<br />
<br />
If the app script has Update() function, it will be called regularly by the game. This allows your apps to do things continuously in the background. The update rate is once every 0.2 seconds, and the game will pass current [[#time|time]] as the parameter.<br />
<br />
The Update function is called regardless of the app's current state, so it's up to you to choose if the app should only do things when it's switched on (which would be the usual case) or if you want the app to continue running in the background.<br />
<br />
If the app table includes a cost value (in contrary to the cost defined in actions), this will get applied every tick the update runs.<br />
<br />
A good example of app using the Update function would be the light monitor app, which checks the light level continuously and then updates that to a [[#Status_window|status window]]:<br />
<br />
<source lang="lua">Update = function()<br />
if state == AppState.on then<br />
lightLevel = Player.GetLightLevel()<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end<br />
end,</source><br />
== Data types ==<br />
<br />
=== Target types ===<br />
<br />
These are the types of targets available from the game's targeting system. Using ''TargetType.None'' allows the app to work without requiring a target at all. The values are used in the game internally for bitmasking multiple target types at the same time, and in general won't be needed for anything in Lua side, you can just use the more convenient text version in your script.<br />
<br />
{| class="wikitable"<br />
!| Type<br />
!align="center"| Value<br />
!| Description<br />
|-<br />
| TargetType.None<br />
|align="center"| 0<br />
| No target. Use for apps which don't require any target at all.<br />
|-<br />
| TargetType.Data<br />
|align="center"| 1<br />
| Any data points currently visible to the player<br />
|-<br />
| TargetType.Interaction<br />
|align="center"| 2<br />
| Physical interactions in the levels<br />
|-<br />
| TargetType.Hackable<br />
|align="center"| 4<br />
| Hackable devices in player's current networks<br />
|-<br />
| TargetType.Character<br />
|align="center"| 8<br />
| People<br />
|}<br />
<br />
=== Menu options ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppMenuState.always<br />
| App is always displayed in this menu.<br />
|-<br />
| AppMenuState.never<br />
| App will never be shown in this menu<br />
|-<br />
| AppMenuState.byDefault<br />
| App will be set as favourite for this menu by default (but can be removed by player)<br />
|-<br />
| AppMenuState.target<br />
| App will always display in Radial menu if current target matches one of the target types for the app.<br />
|}<br />
<br />
=== App states ===<br />
<br />
{| class="wikitable"<br />
!| Name<br />
!| Description<br />
|-<br />
| AppState.unavailable<br />
| The player doesn't have this app yet<br />
|-<br />
| AppState.disabled<br />
| App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off<br />
| App is not doing anything<br />
|-<br />
| AppState.on<br />
| App is switched on and running in the background<br />
|-<br />
| AppState.alert<br />
| App displays alert to notify the player about something<br />
|}<br />
<br />
=== time ===<br />
<br />
The game will pass a ''time'' value to the [[#Update()|Update function]]. This is just counting seconds from when the game was started. (Equal to Unity's [https://docs.unity3d.com/ScriptReference/Time-time.html Time.time])<br />
<br />
=== target ===<br />
<br />
When the game runs the UpdateActions function, or triggers an action, it will pass ''target'' table to the app script. This contains some information about the current target:<br />
<br />
{| class="wikitable"<br />
!| Data<br />
!| Description<br />
|-<br />
| target.name<br />
| the internalName of the current target object<br />
|-<br />
| target.type<br />
| [[#Target_types|TargetType]] of the current target<br />
|-<br />
| target.missionObject<br />
| Set if the target is a MissionObject.(Check the missionObject table for more details)<br />
|-<br />
| target.data<br />
| Set for data targets. Contain s a table with more information (see data table for details)<br />
|-<br />
| target.interactionType<br />
| Set on interaction targets, this will contain the interactionType<br />
|-<br />
| target.macAddress<br />
| Set on device targets. Contains the device's MAC address as a string.<br />
|}<br />
<br />
If the app's targets list includes TargetType.None, and there is no target selected when UpdateAction() or some action is triggered, the target.name will be an empty string, and target.type is set to TargetType.None.<br />
<br />
== Built-in functions ==<br />
<br />
=== Status window ===<br />
<br />
Adding a status window for your app lets you display a small message window for the player near the top left corner of the screen. The windows can have an icon, and a small piece of text. While there's no hard limit on text length (for now, at least), it's best to keep things really short to avoid conflicts with how many other apps the player might be running at the same time, and to allow all of them enough screen space.<br />
<br />
The LightMontior app is a good example of using status windows:<br />
<br />
<source lang="lua">OnStateChange = function(state)<br />
if state == AppState.on then<br />
--UI.ToggleLightMonitor(true)<br />
CreateStatusWindow()<br />
SetStatusIcon(1)<br />
SetStatusIconColor(Color.LightGrey)<br />
DisplayStatusWindow(true)<br />
elseif state == AppState.off then<br />
--UI.ToggleLightMonitor(false)<br />
RemoveStatusWindow()<br />
end<br />
app.state = state<br />
SetState(app.state)<br />
end,<br />
<br />
Update = function()<br />
-- Get player light value<br />
lightLevel = Player.GetLightLevel()<br />
-- change icon color:<br />
if lightLevel < 0.06 then<br />
SetStatusIconColor(Color.Green)<br />
elseif lightLevel < 0.15 then<br />
SetStatusIconColor(Color.Blue)<br />
else<br />
SetStatusIconColor(Color.LightGrey)<br />
end<br />
-- update status text<br />
statusText = string.format("%.1f %%", lightLevel * 100)<br />
UpdateStatusWindow(statusText)<br />
end,</source><br />
So, what happens here is we use OnStateChange() to check if the app has been turned on, and if that's the case then we ask the game to create a status window for the app. It's going to be empty, and invisible, to start with. Next we assign an icon for it (first one form the [[#statusIcons|status icons]] list in the app definition) and set that icon's color to light grey (which matches nicely with rest of the UI content). When all that is done,. we tell the game to actually show the window.<br />
<br />
The, in the Update() function, we check the light level, and based on it we change the status icon's color, and then also parse the light level into a nice format to display to user.<br />
<br />
Finally, in OnStateChange() function, we make sure to remove the status window when the app is switched off.<br />
<br />
We ''could'' just choose to show and hide the window as needed, and in some cases you might want to do that in Update() or something to only display the window part of the time. It's still best to actually remove the window completely when the app is not running.<br />
<br />
All the functions to handle status windows are explained better below:<br />
<br />
==== CreateStatusWindow() ====<br />
<br />
This is just a simple instruction for the game that you want your app to have a status window. No further options, the game will create the window for you, with a default Off Grid logo as icon, and no text. The window will be hidden by default.<br />
<br />
==== DisplayStatusWindow() ====<br />
<br />
Use <code>DisplayStatusWindow(true)</code> and <code>DisplayStatusWindow(false)</code> to show and hide the window.<br />
<br />
==== RemoveStatusWindow() ====<br />
<br />
When a window is not needed any longer, for example when the app is switched off, use this to remove it from the UI.<br />
<br />
==== SetStatusIcon() ====<br />
<br />
SetStatusIcon() sets the status window's icon to one of the icons defined in the statusIcons list in your app definition. Keep in mind that unlike most programming languages, Lua lists start from 1, so to se the first icon you'd run <code>SetStatusIcon(1)</code>.<br />
<br />
==== SetStatusIconColor() ====<br />
<br />
If you want to change the icon color, SetStatusIconColor() lets you do that. As with everything else in Off Grid scripting, you can either define the color as RGBA values, or use one of our pre-defined colors.<br />
<br />
For the best results you should make your original icon image pure white, the color set here will multiply any color values from the icon image with the new color, so anything else than white (or at least grayscale) icon mgiht result in unexpected colors...<br />
<br />
using pre-defined color:<br />
<br />
<source lang="lua">SetStatusIconColor(Color.Blue)</source><br />
using a table to define red, green, blue and alpha values:<br />
<br />
<source lang="lua">SetStatusIconColor({0.2, 0.4, 0.9, 1.0})</source><br />
=== App state ===<br />
<br />
As explained in the [[#OnSTateChange()|OnStateChange()]], to make sure it's always the Lua app that determines what the app does (perhaps excluding some extreme cases like player phone shutting down due to running out of battery etc), nothing on the game side can directly tell the app to change it's state. Instead, the game will ''ask'' the Lua app to do so, and the app can then do it, or choose not to. Once you have determined what the app does, you should then communicate that back to the game, using SetState() function.<br />
<br />
==== SetState() ====<br />
<br />
Use <code>SetState(app.state)</code> to set the App's state on the game side to match the state your Lua script is in at the moment. This will be reflected on the app's icon color in AppWheel/RadialUI, and also in player being able to (or not) to use the app.<br />
<br />
== Apps in your mission and other scripts ==<br />
<br />
Some of the app functionality can be triggered from your other Lua scripts. Most commonly, you'd want use this to make apps available to the player during your mission rather than from the start, but there are of course more creative ways to make use of the API. It's worth remembering, though, that in the end it's the App's Lua script that determines what the app does, anything else can only ''request'' it to do something.<br />
<br />
=== Apps API ===<br />
<br />
The full Apps Lua API is documented [[Apps_Lua_API|here]].<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Mission_Scripting&diff=1617Mission Scripting2022-05-20T11:34:10Z<p>WikiAdmin: /* Mission Start */</p>
<hr />
<div>== Intro ==<br />
<br />
This article will describe everything you need to know in order to understand how to write and modify missions scripts in Off Grid!<br />
Mission scripts can get a little bit large but don't let that overwhelm you, they're deceptively simple, we promise!<br />
<br />
== Pre-requisites ==<br />
All modding scripts for Off Grid are written in Lua, it's a lovely little language which is simple ( ''don't tell it I said that'' ) and extensible thanks to the marvellous [http://www.moonsharp.org MoonSharp].<br />
<br />
Lua is quite easy to pick up, even people with no programming experience should be able to get hacking in next to no time!<br />
<br />
You'll need the following tools for editing and writing new mission scripts:<br />
<br />
=== IDE ===<br />
Firstly you'll need an [https://en.wikipedia.org/wiki/Integrated_development_environment IDE], we recommend the [https://atom.io/ Atom] with the following extensions:<br />
{| class="wikitable"<br />
| [https://atom.io/packages/language-lua language-lua] || Syntax highlighting for Lua<br />
|-<br />
| [https://atom.io/packages/autocomplete-lua autocomplete-lua] || Auto-complete support for Lua, we recommend enabling the "Override lower priority providers" in the plugin settings<br />
|-<br />
|}<br />
<br />
You might also want to get our [[Atom Snippets]] code and copy&paste it into Atom's Snippets file to add nice autocompletion for Off Grid API.<br />
<br />
=== LevelKit & Game ===<br />
Both of these are needed in order to run and test and scripts you create<br />
<br />
'''TO DO'''<br />
<br />
== The Mission Script Structure ==<br />
As mentioned above, mission scripts can get rather large so this article will attempt to break down all the little sub elements, it can be helpful to try and think of the mission script in the same way you might think of a film script.<br />
The vast majority of the script is simply defining what is in the world of the mission you've created.<br />
Enough talking, lets jump into an example! Rather than posting an entire example script, we'll go from top to bottom exploring what each element does.<br />
<br />
=== Mission table ===<br />
<br />
==== Initial Mission State ====<br />
<syntaxhighlight source lang="lua" line><br />
name = "MyNewMission",<br />
description = "The best mod ever",<br />
startTime = "27/04/2009/21:30",<br />
<br />
</syntaxhighlight><br />
<br />
In this first code block we create the ''mission'' Lua table and define its first value.<br />
<br />
{| class="wikitable"<br />
! Name !! Description<br />
|-<br />
| ''startTime'' || The start time of the mission in the game world (Day/Month/Year/Hour:Mins)<br />
|}<br />
<br />
startTime doesn't have to (and probaly shouldn't) be the current date, you can set your mission in any time you desire.<br />
<br />
==== State ====<br />
<br />
State includes anything you'd like to be saved & loaded. So for instance, you might want to set a bool on the first time a player enters a building, or performs an action, or completes a particular objective. Currently we only support numbers, bools and strings, and these need to have string keys (rather than indices).<br />
<br />
<syntaxhighlight source lang="lua" line start=5><br />
state = {<br />
hasEnteredBuilding = true,<br />
sideObjectivesComplete = 17,<br />
mostAnnoyedGuard = "Brian",<br />
},<br />
</syntaxhighlight><br />
<br />
==== Characters ====<br />
<br />
<syntaxhighlight source lang="lua" line start=5><br />
-- Character definitions:<br />
characters = {<br />
player = {<br />
displayName = "Joe Harman",<br />
characterType = "player",<br />
prefab = "player",<br />
spawnpoint = "PlayerSpawn",<br />
},<br />
},<br />
</syntaxhighlight><br />
<br />
This is the first example of us creating a sub table in the ''mission'' table, the ''characters'' table contains all the information about the characters in your mission! Different types of characters might need different values, and there are some optional settings as well for different purposes. Different character types and creating background profiles for the characters is explained in more detail in [[Character Profiles]].<br />
<br />
In this example we've added Joe, the protagonist of Off Grid. Here's a break down of what the value of Joe's table means:<br />
{| class="wikitable"<br />
!colspan="2" | Character Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''displayName'' || The name that will be used any game UI referencing the character <br />
|- <br />
| ''characterType'' || The 'type' of the character, see [[Character Types and Prefabs]] for more information on possible values<br />
|-<br />
| 'prefab' || The prefab for the character, see [[Character Types and Prefabs]] for more information on possible values<br />
|-<br />
| ''spawnpoint'' || The name of the spawn mission object, the position of this object will be where the character is spawned<br />
|}<br />
<br />
The ''characters'' table can hold information on as many characters as you'd like to fill the mission with, they won't be spawned into the mission until you decide (more on that later)<br />
<br />
==== Physical Items ====<br />
<br />
<syntaxhighlight source lang="lua" line start=15><br />
-- Inventory items:<br />
items = {<br />
usbkey = {<br />
displayName = "USB dongle",<br />
description = "Modified USB Bluetooth dongle given to you by the hackers",<br />
uiSpriteName = "usbdongle.png",<br />
onItemUse = function()<br />
// Do stuff.<br />
end,<br />
},<br />
},<br />
</syntaxhighlight><br />
<br />
The ''items'' table contains information about physical items that the player will be able to add to their inventory over the course of the mission, in this example we're marking up a USB dongle that the player can plug into computers<br />
{| class="wikitable"<br />
!colspan="2" | Item Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''displayName'' || The display name of the item, this is what will be displayed in the games UI<br />
|- <br />
| ''description'' || The description of the item, this will be displayed to users in the players inventory UI<br />
|-<br />
| ''uiSpriteName'' || The path to the image file to be used for the item in game menus.<br />
|-<br />
| ''onItemUse'' || Optional. Function that's ran when the player selects to use the item in inventory menu.<br />
|}<br />
<br />
==== Data ====<br />
<br />
<syntaxhighlight source lang="lua" line start=24><br />
--[[ Data files:<br />
Available data types: generic, text, SMS, encrypted, audio, video, location, key, UUID<br />
]]--<br />
data = {<br />
PlayerPGPKey = {<br />
name = "Personal PGP encryption key",<br />
immutable = true,<br />
dataType = DataType.Key,<br />
creatorName = "Player",<br />
dataString = "PGP Fingerprint: 1d7d ef54 7a63 5756 63a7 cf14 fbd8 775c c39d 4e51",<br />
description = "AES 256-bit",<br />
dataColor = {0.0, 0.6, 1.0, 0.3},<br />
},<br />
<br />
},<br />
</syntaxhighlight><br />
<br />
The data table contains data that's specific to your mission, in this example we're defining the players [https://en.wikipedia.org/wiki/Pretty_Good_Privacy PGP] encryption key <br />
<br />
{| class="wikitable"<br />
!colspan="2"| Data Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''name'' || The display name of the data, this is what will be displayed in the games UI<br />
|-<br />
| ''immutable'' || Optional. Is the data immutable? If true the player won't be able to delete it, this is useful for data items that are part of mission objectives<br />
|- <br />
| ''dataType'' || The type of the data, this is used for displaying the data correctly. Available DataTypes are listed in [[Constants_Lua_API#DataType|Constants page]].<br />
|- <br />
| ''creatorName'' || The name of the data's creator (displayed in the UI)<br />
|-<br />
| ''dataString'' || The contents of the data file<br />
|- <br />
| ''description'' || A description of the data file<br />
|-<br />
| ''dataColor'' || RGBA value that's used as the colour of the data point when visible in the players data view<br />
|-<br />
| ''script'' || The path (relative to the mission folder) to the optional Lua script for this data. See [[Data Scripting]] for more information.<br />
|}<br />
<br />
==== Networks ====<br />
<br />
<syntaxhighlight source lang="lua" line start=40><br />
-- Networks:<br />
networks = {<br />
semaeopus4G = {<br />
name = "Semaeopus 4G",<br />
networkType = NetworkType.mobile,<br />
allowPlayerDisconnect = false,<br />
},<br />
},<br />
</syntaxhighlight><br />
<br />
The networks table contains all the information about the possible networks in your game, networks can be of different types such as mobile networks, WiFi, and mesh<br />
{| class="wikitable"<br />
!colspan="2" | Network Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''name'' || The name of the network (Must be unique!)<br />
|-<br />
| ''networkType'' || The type of the network.<br />
|-<br />
| ''allowPlayerDisconnect'' || Is the player allowed to disconnect from the network?<br />
|}<br />
<br />
Data sent at higher access levels across a network won't be visible to devices that are connected at a lower access level.<br />
<br />
==== Devices ====<br />
<br />
<syntaxhighlight source lang="lua" line start=66><br />
devices = {<br />
laptop = {<br />
owner = "ownerInternalName",<br />
dataColor = Color.Orange,<br />
script = "Scripts/Devices/laptop.lua",<br />
},<br />
},<br />
</syntaxhighlight><br />
<br />
{| class="wikitable"<br />
!colspan="2" | Device Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''dataColor'' || The color used when displaying data from this device.<br />
|-<br />
| ''owner'' || Optional. The internalName of the device's owner. Use this if you want too restrict device's access based on owner's metadata or something.<br />
|-<br />
| ''script'' || The script that defines the GUI and the behaviour of the device ( See [[Device Scripting | device scripts]] for more information )<br />
|}<br />
<br />
==== Objectives ====<br />
<br />
<syntaxhighlight source lang="lua" line start=52><br />
-- Mission objectives:<br />
objectives = {<br />
enterTheBuilding = {<br />
name = "Enter the building",<br />
description = "Short description of the objective",<br />
active = false,<br />
visible = true,<br />
keyObjective = false,<br />
<br />
onStart = function()<br />
print("Player must now enter the building")<br />
end,<br />
onCompleted = function()<br />
print("The player is now in the building!")<br />
end,<br />
},<br />
},<br />
</syntaxhighlight><br />
<br />
The objectives table contains all the tasks that the player should complete before your mission is finished.<br />
<br />
{| class="wikitable"<br />
!colspan="2" | Objective Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''name'' || The name of the objective, displayed to the player.<br />
|-<br />
| ''description'' || Short description displayed to the player in the Tasks menu.<br />
|-<br />
| ''active'' || Is the objective active at the moment (as opposed to already completed, or not even started yet)? (Optional. This is handled automatically when you call Mission.StartObjective() and Mission.CompleteObjective()) <br />
|-<br />
| ''visible'' || Should this objective be displayed to player? Hidden objectives can help organizing your mission scripts. (Optional, defaults to visible)<br />
|-<br />
| ''keyObjective'' || Key objectives are ones the player knows from the start of the level, they are visible even before the actual objective becomes active. (Optional)<br />
|-<br />
| ''onStart'' ||A callback function that's triggered when the objective is started. (Optional)<br />
|-<br />
| ''onCompleted'' || A callback function that's triggered when the objective is completed (Optional)<br />
|}<br />
<br />
=== Starting the mission ===<br />
<br />
In addition to the big mission table, you'll need two more things to get a mission up and running; MissionSetup() and MissionStart() functions.<br />
<br />
==== MissionSetup_Always() ====<br />
MissionSetup_Always() is executed every time the mission is started, be it a fresh start, or when loading a saved game.<br />
<br />
<syntaxhighlight source lang="lua" line start=73><br />
function MissionSetup_Always()<br />
<br />
-- add player and guards:<br />
Mission.SpawnCharacter("player")<br />
-- these two below add in the guards which you might not have created yet!<br />
Mission.SpawnCharacter("jack")<br />
Mission.SpawnCharacter("john")<br />
<br />
-- Doors:<br />
Doors.SetNetwork("ApostleWiFi")<br />
<br />
Doors.SetZoneKeys("Maintenance", {"Maintenance", "Staff", "Security", "Admin"})<br />
Doors.SetZoneKeys("Staff", {"Staff", "Security", "Admin"})<br />
Doors.SetZoneKeys("Security", {"Security", "Admin"})<br />
Doors.SetZoneKeys("Admin", {"Admin"})<br />
<br />
Mission.CompletedMissionSetup_Always()<br />
end<br />
</syntaxhighlight><br />
<br />
This will add in all of your created elements into your level, below you will see what the function of each part of this code will do.<br />
<br />
===== Adding in Characters =====<br />
<br />
<syntaxhighlight source lang="lua" line start=74><br />
-- add player and guards:<br />
Mission.SpawnCharacter("player")<br />
-- these two below add in the guards which you might not have created yet!<br />
Mission.SpawnCharacter("jack")<br />
Mission.SpawnCharacter("john")<br />
</syntaxhighlight><br />
<br />
This part of the code shows the format of spawning in characters - this includes the player and any NPCs like the guards, which you will have created further up in your Mission Script. If the title of your character table for your player was called <code>jenson</code>, you would simply change <code>Mission.SpawnCharacter("player")</code> to <code>Mission.SpawnCharacter("jenson")</code>, this would now let your new character spawn in.<br />
<br />
<br />
===== Connect Doors to the Network and set Zones =====<br />
Once you have [[Setting up Doors|created doors]] you can then also add them to a network. Network-connected doors arre controlled by the electronic lock systems in the buildings and opened by specific access keys. Firstly you add the door to a network, then you can set the door's zone to require a specific key to open it. For example where if you consider the following command to set keys: <code>Doors.SetZoneKeys("Offices", {"admins", "janitors"})</code>, <code>"Offices"</code> is the name of the zone the door is assigned to, and <code>{"admins", "janitors"}</code> telsl that all doors belonging to this zone should be possible to open using either "admin" or "janitors" keys.<br />
<br />
==== MissionSetup_NoSave() ====<br />
<br />
MissionSetup_NoSave() is ran after MissionSetup_Always() when the mission is started form the beginning. When loading a save or checkpoint, the same things would be covered by data loaded from the save instead. Use this to set initial state of things in your level.<br />
<br />
<syntaxhighlight source lang="lua" line start=0><br />
function MissionSetup_NoSave()<br />
<br />
-- Add items to player inventory:<br />
Player.ClearInventory()<br />
Player.AddItemToInventory("usbkey")<br />
Player.AddItemToInventory("meshnode")<br />
<br />
-- Add files to data inventory:<br />
Player.ClearDataFiles()<br />
Player.AddDataFile("PlayerPGPKey", mission.data.PlayerPGPKey)<br />
Player.AddDataFile("CoffeeOffer", mission.data.CoffeeOffer)<br />
<br />
--mobile<br />
Network.ConnectToNetwork(<br />
{<br />
"player",<br />
"DevLaptop",<br />
"Smedley",<br />
"Terrence",<br />
"securityChief",<br />
"apostleItIntern",<br />
"guard1","guard2","guardF1a","guardF1b","guardF1c","guard4","guard5",<br />
"drone1",<br />
"davesEPhone",<br />
"BasementSmartTV",<br />
"BasementLaptop",<br />
"iDeskLampTEST",<br />
"testRadio",<br />
"SmartFridge",<br />
"sodamachine",<br />
},<br />
"Semaeopus4G",<br />
"user"<br />
)<br />
<br />
-- WIFI<br />
Network.ConnectToNetwork("player", "ApostleWiFi", "none" )<br />
Network.ConnectToNetwork(<br />
{<br />
"guard1","guard2","guardF1a","guardF1b","guardF1c","guard4","guard5",<br />
},<br />
"ApostleWiFi",<br />
"user"<br />
)<br />
<br />
-- MESH<br />
Network.ConnectToNetwork( {"Smedley","MeshleaksSecureDrop"}, "MESH", "admin" )<br />
<br />
-- Doors<br />
Doors.SetKeyOnDevice("Maintenance", "IDcard01")<br />
Doors.SetKeyOnDevice("Admin", "IDcard02")<br />
<br />
Doors.AssignKeyToCharacter("Security", "guard1")<br />
Doors.AssignKeyToCharacter("Security", "guard2")<br />
<br />
<br />
-- Set up music (we'll set it to silent in start, and set gameplay state from a trigger when entering lobby)<br />
Sound.TriggerEvent("Set_MX_Mission_1")<br />
Sound.TriggerEvent("Set_State_MX_Mission_Silent")<br />
<br />
---[[ Things to enable when testing & debugging:<br />
Apps.RequestAppState("DevTools", AppState.off)<br />
--]]<br />
<br />
Mission.CompletedMissionSetup_NoSave()<br />
<br />
end<br />
</syntaxhighlight><br />
<br />
== Including other files ==<br />
<br />
We know how long these mission scripts can become. As a result, we came up with a method of splitting up your mission into smaller, bite-size pieces. You're more than welcome to keep your mission in a single file, but if you want to manage them differently, read on...<br />
<br />
Let's say you want to keep all your device related shenanigans in a separate file for clarity. We'll call this 'mission_devices.lua', and put it in the same folder as the mission.<br />
<br />
<syntaxhighlight source lang="lua" line start=0><br />
MyDevices = {<br />
somedevice = {<br />
internalName = "Device Z",<br />
script = "device.lua"<br />
},<br />
someotherdevice = {<br />
internalName = "Another Device",<br />
script = "device.lua"<br />
},<br />
}<br />
</syntaxhighlight><br />
<br />
Now, this obviously seems rather pointless as there are only two devices. But if there were many, many more, it could prove quite useful.<br />
<br />
To use this in the mission.lua file, we need to utilise Lua's dofile() function.<br />
<br />
<syntaxhighlight source lang="lua" line start=0><br />
dofile("mission_devices.lua")<br />
<br />
mission = {<br />
-- some tables!<br />
devices = MyDevices<br />
-- lots of other tables!<br />
}<br />
<br />
</syntaxhighlight><br />
<br />
The key bit here is that the devices table is now defined to be the table we wrote in mission_devices.lua. If we took this to its natural conclusion, we end up with the following:<br />
<br />
<syntaxhighlight source lang="lua" line start=0><br />
dofile("mission_characters.lua")<br />
dofile("mission_devices.lua")<br />
dofile("mission_data.lua")<br />
dofile("mission_items.lua")<br />
dofile("mission_networks.lua")<br />
<br />
mission = {<br />
character = MyCharacters,<br />
devices = MyDevices,<br />
data = MyData,<br />
items = MyItems,<br />
networks = MyNetworks,<br />
objectives = {<br />
-- objectives here...<br />
},<br />
}<br />
<br />
</syntaxhighlight><br />
<br />
We hope this proves useful in organising and managing your creations!<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Mission_Scripting&diff=1616Mission Scripting2022-05-20T11:32:12Z<p>WikiAdmin: /* Mission Setup */</p>
<hr />
<div>== Intro ==<br />
<br />
This article will describe everything you need to know in order to understand how to write and modify missions scripts in Off Grid!<br />
Mission scripts can get a little bit large but don't let that overwhelm you, they're deceptively simple, we promise!<br />
<br />
== Pre-requisites ==<br />
All modding scripts for Off Grid are written in Lua, it's a lovely little language which is simple ( ''don't tell it I said that'' ) and extensible thanks to the marvellous [http://www.moonsharp.org MoonSharp].<br />
<br />
Lua is quite easy to pick up, even people with no programming experience should be able to get hacking in next to no time!<br />
<br />
You'll need the following tools for editing and writing new mission scripts:<br />
<br />
=== IDE ===<br />
Firstly you'll need an [https://en.wikipedia.org/wiki/Integrated_development_environment IDE], we recommend the [https://atom.io/ Atom] with the following extensions:<br />
{| class="wikitable"<br />
| [https://atom.io/packages/language-lua language-lua] || Syntax highlighting for Lua<br />
|-<br />
| [https://atom.io/packages/autocomplete-lua autocomplete-lua] || Auto-complete support for Lua, we recommend enabling the "Override lower priority providers" in the plugin settings<br />
|-<br />
|}<br />
<br />
You might also want to get our [[Atom Snippets]] code and copy&paste it into Atom's Snippets file to add nice autocompletion for Off Grid API.<br />
<br />
=== LevelKit & Game ===<br />
Both of these are needed in order to run and test and scripts you create<br />
<br />
'''TO DO'''<br />
<br />
== The Mission Script Structure ==<br />
As mentioned above, mission scripts can get rather large so this article will attempt to break down all the little sub elements, it can be helpful to try and think of the mission script in the same way you might think of a film script.<br />
The vast majority of the script is simply defining what is in the world of the mission you've created.<br />
Enough talking, lets jump into an example! Rather than posting an entire example script, we'll go from top to bottom exploring what each element does.<br />
<br />
=== Mission table ===<br />
<br />
==== Initial Mission State ====<br />
<syntaxhighlight source lang="lua" line><br />
name = "MyNewMission",<br />
description = "The best mod ever",<br />
startTime = "27/04/2009/21:30",<br />
<br />
</syntaxhighlight><br />
<br />
In this first code block we create the ''mission'' Lua table and define its first value.<br />
<br />
{| class="wikitable"<br />
! Name !! Description<br />
|-<br />
| ''startTime'' || The start time of the mission in the game world (Day/Month/Year/Hour:Mins)<br />
|}<br />
<br />
startTime doesn't have to (and probaly shouldn't) be the current date, you can set your mission in any time you desire.<br />
<br />
==== State ====<br />
<br />
State includes anything you'd like to be saved & loaded. So for instance, you might want to set a bool on the first time a player enters a building, or performs an action, or completes a particular objective. Currently we only support numbers, bools and strings, and these need to have string keys (rather than indices).<br />
<br />
<syntaxhighlight source lang="lua" line start=5><br />
state = {<br />
hasEnteredBuilding = true,<br />
sideObjectivesComplete = 17,<br />
mostAnnoyedGuard = "Brian",<br />
},<br />
</syntaxhighlight><br />
<br />
==== Characters ====<br />
<br />
<syntaxhighlight source lang="lua" line start=5><br />
-- Character definitions:<br />
characters = {<br />
player = {<br />
displayName = "Joe Harman",<br />
characterType = "player",<br />
prefab = "player",<br />
spawnpoint = "PlayerSpawn",<br />
},<br />
},<br />
</syntaxhighlight><br />
<br />
This is the first example of us creating a sub table in the ''mission'' table, the ''characters'' table contains all the information about the characters in your mission! Different types of characters might need different values, and there are some optional settings as well for different purposes. Different character types and creating background profiles for the characters is explained in more detail in [[Character Profiles]].<br />
<br />
In this example we've added Joe, the protagonist of Off Grid. Here's a break down of what the value of Joe's table means:<br />
{| class="wikitable"<br />
!colspan="2" | Character Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''displayName'' || The name that will be used any game UI referencing the character <br />
|- <br />
| ''characterType'' || The 'type' of the character, see [[Character Types and Prefabs]] for more information on possible values<br />
|-<br />
| 'prefab' || The prefab for the character, see [[Character Types and Prefabs]] for more information on possible values<br />
|-<br />
| ''spawnpoint'' || The name of the spawn mission object, the position of this object will be where the character is spawned<br />
|}<br />
<br />
The ''characters'' table can hold information on as many characters as you'd like to fill the mission with, they won't be spawned into the mission until you decide (more on that later)<br />
<br />
==== Physical Items ====<br />
<br />
<syntaxhighlight source lang="lua" line start=15><br />
-- Inventory items:<br />
items = {<br />
usbkey = {<br />
displayName = "USB dongle",<br />
description = "Modified USB Bluetooth dongle given to you by the hackers",<br />
uiSpriteName = "usbdongle.png",<br />
onItemUse = function()<br />
// Do stuff.<br />
end,<br />
},<br />
},<br />
</syntaxhighlight><br />
<br />
The ''items'' table contains information about physical items that the player will be able to add to their inventory over the course of the mission, in this example we're marking up a USB dongle that the player can plug into computers<br />
{| class="wikitable"<br />
!colspan="2" | Item Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''displayName'' || The display name of the item, this is what will be displayed in the games UI<br />
|- <br />
| ''description'' || The description of the item, this will be displayed to users in the players inventory UI<br />
|-<br />
| ''uiSpriteName'' || The path to the image file to be used for the item in game menus.<br />
|-<br />
| ''onItemUse'' || Optional. Function that's ran when the player selects to use the item in inventory menu.<br />
|}<br />
<br />
==== Data ====<br />
<br />
<syntaxhighlight source lang="lua" line start=24><br />
--[[ Data files:<br />
Available data types: generic, text, SMS, encrypted, audio, video, location, key, UUID<br />
]]--<br />
data = {<br />
PlayerPGPKey = {<br />
name = "Personal PGP encryption key",<br />
immutable = true,<br />
dataType = DataType.Key,<br />
creatorName = "Player",<br />
dataString = "PGP Fingerprint: 1d7d ef54 7a63 5756 63a7 cf14 fbd8 775c c39d 4e51",<br />
description = "AES 256-bit",<br />
dataColor = {0.0, 0.6, 1.0, 0.3},<br />
},<br />
<br />
},<br />
</syntaxhighlight><br />
<br />
The data table contains data that's specific to your mission, in this example we're defining the players [https://en.wikipedia.org/wiki/Pretty_Good_Privacy PGP] encryption key <br />
<br />
{| class="wikitable"<br />
!colspan="2"| Data Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''name'' || The display name of the data, this is what will be displayed in the games UI<br />
|-<br />
| ''immutable'' || Optional. Is the data immutable? If true the player won't be able to delete it, this is useful for data items that are part of mission objectives<br />
|- <br />
| ''dataType'' || The type of the data, this is used for displaying the data correctly. Available DataTypes are listed in [[Constants_Lua_API#DataType|Constants page]].<br />
|- <br />
| ''creatorName'' || The name of the data's creator (displayed in the UI)<br />
|-<br />
| ''dataString'' || The contents of the data file<br />
|- <br />
| ''description'' || A description of the data file<br />
|-<br />
| ''dataColor'' || RGBA value that's used as the colour of the data point when visible in the players data view<br />
|-<br />
| ''script'' || The path (relative to the mission folder) to the optional Lua script for this data. See [[Data Scripting]] for more information.<br />
|}<br />
<br />
==== Networks ====<br />
<br />
<syntaxhighlight source lang="lua" line start=40><br />
-- Networks:<br />
networks = {<br />
semaeopus4G = {<br />
name = "Semaeopus 4G",<br />
networkType = NetworkType.mobile,<br />
allowPlayerDisconnect = false,<br />
},<br />
},<br />
</syntaxhighlight><br />
<br />
The networks table contains all the information about the possible networks in your game, networks can be of different types such as mobile networks, WiFi, and mesh<br />
{| class="wikitable"<br />
!colspan="2" | Network Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''name'' || The name of the network (Must be unique!)<br />
|-<br />
| ''networkType'' || The type of the network.<br />
|-<br />
| ''allowPlayerDisconnect'' || Is the player allowed to disconnect from the network?<br />
|}<br />
<br />
Data sent at higher access levels across a network won't be visible to devices that are connected at a lower access level.<br />
<br />
==== Devices ====<br />
<br />
<syntaxhighlight source lang="lua" line start=66><br />
devices = {<br />
laptop = {<br />
owner = "ownerInternalName",<br />
dataColor = Color.Orange,<br />
script = "Scripts/Devices/laptop.lua",<br />
},<br />
},<br />
</syntaxhighlight><br />
<br />
{| class="wikitable"<br />
!colspan="2" | Device Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''dataColor'' || The color used when displaying data from this device.<br />
|-<br />
| ''owner'' || Optional. The internalName of the device's owner. Use this if you want too restrict device's access based on owner's metadata or something.<br />
|-<br />
| ''script'' || The script that defines the GUI and the behaviour of the device ( See [[Device Scripting | device scripts]] for more information )<br />
|}<br />
<br />
==== Objectives ====<br />
<br />
<syntaxhighlight source lang="lua" line start=52><br />
-- Mission objectives:<br />
objectives = {<br />
enterTheBuilding = {<br />
name = "Enter the building",<br />
description = "Short description of the objective",<br />
active = false,<br />
visible = true,<br />
keyObjective = false,<br />
<br />
onStart = function()<br />
print("Player must now enter the building")<br />
end,<br />
onCompleted = function()<br />
print("The player is now in the building!")<br />
end,<br />
},<br />
},<br />
</syntaxhighlight><br />
<br />
The objectives table contains all the tasks that the player should complete before your mission is finished.<br />
<br />
{| class="wikitable"<br />
!colspan="2" | Objective Table<br />
|- <br />
! Name !! Description<br />
|-<br />
| ''name'' || The name of the objective, displayed to the player.<br />
|-<br />
| ''description'' || Short description displayed to the player in the Tasks menu.<br />
|-<br />
| ''active'' || Is the objective active at the moment (as opposed to already completed, or not even started yet)? (Optional. This is handled automatically when you call Mission.StartObjective() and Mission.CompleteObjective()) <br />
|-<br />
| ''visible'' || Should this objective be displayed to player? Hidden objectives can help organizing your mission scripts. (Optional, defaults to visible)<br />
|-<br />
| ''keyObjective'' || Key objectives are ones the player knows from the start of the level, they are visible even before the actual objective becomes active. (Optional)<br />
|-<br />
| ''onStart'' ||A callback function that's triggered when the objective is started. (Optional)<br />
|-<br />
| ''onCompleted'' || A callback function that's triggered when the objective is completed (Optional)<br />
|}<br />
<br />
=== Starting the mission ===<br />
<br />
In addition to the big mission table, you'll need two more things to get a mission up and running; MissionSetup() and MissionStart() functions.<br />
<br />
==== MissionSetup_Always() ====<br />
MissionSetup_Always() is executed every time the mission is started, be it a fresh start, or when loading a saved game.<br />
<br />
<syntaxhighlight source lang="lua" line start=73><br />
function MissionSetup_Always()<br />
<br />
-- add player and guards:<br />
Mission.SpawnCharacter("player")<br />
-- these two below add in the guards which you might not have created yet!<br />
Mission.SpawnCharacter("jack")<br />
Mission.SpawnCharacter("john")<br />
<br />
-- Doors:<br />
Doors.SetNetwork("ApostleWiFi")<br />
<br />
Doors.SetZoneKeys("Maintenance", {"Maintenance", "Staff", "Security", "Admin"})<br />
Doors.SetZoneKeys("Staff", {"Staff", "Security", "Admin"})<br />
Doors.SetZoneKeys("Security", {"Security", "Admin"})<br />
Doors.SetZoneKeys("Admin", {"Admin"})<br />
<br />
Mission.CompletedMissionSetup_Always()<br />
end<br />
</syntaxhighlight><br />
<br />
This will add in all of your created elements into your level, below you will see what the function of each part of this code will do.<br />
<br />
===== Adding in Characters =====<br />
<br />
<syntaxhighlight source lang="lua" line start=74><br />
-- add player and guards:<br />
Mission.SpawnCharacter("player")<br />
-- these two below add in the guards which you might not have created yet!<br />
Mission.SpawnCharacter("jack")<br />
Mission.SpawnCharacter("john")<br />
</syntaxhighlight><br />
<br />
This part of the code shows the format of spawning in characters - this includes the player and any NPCs like the guards, which you will have created further up in your Mission Script. If the title of your character table for your player was called <code>jenson</code>, you would simply change <code>Mission.SpawnCharacter("player")</code> to <code>Mission.SpawnCharacter("jenson")</code>, this would now let your new character spawn in.<br />
<br />
<br />
===== Connect Doors to the Network and set Zones =====<br />
Once you have [[Setting up Doors|created doors]] you can then also add them to a network. Network-connected doors arre controlled by the electronic lock systems in the buildings and opened by specific access keys. Firstly you add the door to a network, then you can set the door's zone to require a specific key to open it. For example where if you consider the following command to set keys: <code>Doors.SetZoneKeys("Offices", {"admins", "janitors"})</code>, <code>"Offices"</code> is the name of the zone the door is assigned to, and <code>{"admins", "janitors"}</code> telsl that all doors belonging to this zone should be possible to open using either "admin" or "janitors" keys.<br />
<br />
==== Mission Start ====<br />
<br />
StartMission() is ran when the mission is started form the beginning. When loading a save or checkpoint, the same things would be covered by data loaded from the save instead. Use this to set initial state of things in your level.<br />
<br />
<syntaxhighlight source lang="lua" line start=0><br />
function StartMission()<br />
<br />
-- Add items to player inventory:<br />
Player.ClearInventory()<br />
Player.AddItemToInventory("usbkey")<br />
Player.AddItemToInventory("meshnode")<br />
<br />
-- Add files to data inventory:<br />
Player.ClearDataFiles()<br />
Player.AddDataFile("PlayerPGPKey", mission.data.PlayerPGPKey)<br />
Player.AddDataFile("CoffeeOffer", mission.data.CoffeeOffer)<br />
<br />
--mobile<br />
Network.ConnectToNetwork(<br />
{<br />
"player",<br />
"DevLaptop",<br />
"Smedley",<br />
"Terrence",<br />
"securityChief",<br />
"apostleItIntern",<br />
"guard1","guard2","guardF1a","guardF1b","guardF1c","guard4","guard5",<br />
"drone1",<br />
"davesEPhone",<br />
"BasementSmartTV",<br />
"BasementLaptop",<br />
"iDeskLampTEST",<br />
"testRadio",<br />
"SmartFridge",<br />
"sodamachine",<br />
},<br />
"Semaeopus4G",<br />
"user"<br />
)<br />
<br />
-- WIFI<br />
Network.ConnectToNetwork("player", "ApostleWiFi", "none" )<br />
Network.ConnectToNetwork(<br />
{<br />
"guard1","guard2","guardF1a","guardF1b","guardF1c","guard4","guard5",<br />
},<br />
"ApostleWiFi",<br />
"user"<br />
)<br />
<br />
-- MESH<br />
Network.ConnectToNetwork( {"Smedley","MeshleaksSecureDrop"}, "MESH", "admin" )<br />
<br />
Mission.DevicesConnected()<br />
<br />
-- Doors<br />
Doors.SetKeyOnDevice("Maintenance", "IDcard01")<br />
Doors.SetKeyOnDevice("Admin", "IDcard02")<br />
<br />
Doors.AssignKeyToCharacter("Security", "guard1")<br />
Doors.AssignKeyToCharacter("Security", "guard2")<br />
<br />
<br />
-- Set up music (we'll set it to silent in start, and set gameplay state from a trigger when entering lobby)<br />
Sound.TriggerEvent("Set_MX_Mission_1")<br />
Sound.TriggerEvent("Set_State_MX_Mission_Silent")<br />
<br />
---[[ Things to enable when testing & debugging:<br />
Apps.RequestAppState("DevTools", AppState.off)<br />
--]]<br />
<br />
end<br />
</syntaxhighlight><br />
<br />
== Including other files ==<br />
<br />
We know how long these mission scripts can become. As a result, we came up with a method of splitting up your mission into smaller, bite-size pieces. You're more than welcome to keep your mission in a single file, but if you want to manage them differently, read on...<br />
<br />
Let's say you want to keep all your device related shenanigans in a separate file for clarity. We'll call this 'mission_devices.lua', and put it in the same folder as the mission.<br />
<br />
<syntaxhighlight source lang="lua" line start=0><br />
MyDevices = {<br />
somedevice = {<br />
internalName = "Device Z",<br />
script = "device.lua"<br />
},<br />
someotherdevice = {<br />
internalName = "Another Device",<br />
script = "device.lua"<br />
},<br />
}<br />
</syntaxhighlight><br />
<br />
Now, this obviously seems rather pointless as there are only two devices. But if there were many, many more, it could prove quite useful.<br />
<br />
To use this in the mission.lua file, we need to utilise Lua's dofile() function.<br />
<br />
<syntaxhighlight source lang="lua" line start=0><br />
dofile("mission_devices.lua")<br />
<br />
mission = {<br />
-- some tables!<br />
devices = MyDevices<br />
-- lots of other tables!<br />
}<br />
<br />
</syntaxhighlight><br />
<br />
The key bit here is that the devices table is now defined to be the table we wrote in mission_devices.lua. If we took this to its natural conclusion, we end up with the following:<br />
<br />
<syntaxhighlight source lang="lua" line start=0><br />
dofile("mission_characters.lua")<br />
dofile("mission_devices.lua")<br />
dofile("mission_data.lua")<br />
dofile("mission_items.lua")<br />
dofile("mission_networks.lua")<br />
<br />
mission = {<br />
character = MyCharacters,<br />
devices = MyDevices,<br />
data = MyData,<br />
items = MyItems,<br />
networks = MyNetworks,<br />
objectives = {<br />
-- objectives here...<br />
},<br />
}<br />
<br />
</syntaxhighlight><br />
<br />
We hope this proves useful in organising and managing your creations!<br />
<br />
[[Category:Modding]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Light_Groups&diff=1610Light Groups2021-10-25T15:20:15Z<p>WikiAdmin: </p>
<hr />
<div>#Create lights<br />
#Add a light controller (LightHub-01 prefab form LevelKit, for example)<br />
#At the bottom of the light controller's MissionObject component, add your lights to the list<br />
#Add the light controller as a device in your mission Lua script (you can use the Common LightHub.lua script for simple setup!) and connect to a network.<br />
#Profit.<br />
<br />
Setting the light controller device active/inactive will toggle all it's lighs on & off.</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Light_Groups&diff=1609Light Groups2021-10-25T15:19:30Z<p>WikiAdmin: Created page with "1. Create lights 2. Add a light controller (LightHub-01 prefab form LevelKit, for example) 3. At the bottom of the light controller's MissionObject component, add your lights..."</p>
<hr />
<div>1. Create lights<br />
2. Add a light controller (LightHub-01 prefab form LevelKit, for example)<br />
3. At the bottom of the light controller's MissionObject component, add your lights to the list<br />
4. Add the light controller as a device in your mission Lua script (you can use the Common LightHub.lua script for simple setup!) and connect to a network.<br />
5. Profit.<br />
<br />
Setting the light controller device active/inactive will toggle all it's lighs on & off.</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=System_Lua_API&diff=1608System Lua API2021-10-14T09:12:27Z<p>WikiAdmin: Created page with "<!-- This file is auto generated, please don't edit manually! --> = System = == Description == System API provides access to game settings and other generic system features ==..."</p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= System =<br />
== Description ==<br />
System API provides access to game settings and other generic system features<br />
== Functions ==<br />
=== SetLanguage ===<br />
<syntaxhighlight source lang="lua">System.SetLanguage(langCode)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| langCode || string<br />
|}<br />
'''Description''': Sets the game language<br />
<br />
'''Returns''': Nothing<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Debug_Lua_API&diff=1607Debug Lua API2021-10-14T09:11:49Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Debug =<br />
== Description ==<br />
Debugging & Testing tools<br />
== Functions ==<br />
=== ConnectToDevLaptop ===<br />
<syntaxhighlight source lang="lua">Debug.ConnectToDevLaptop()</syntaxhighlight><br />
'''Description''': Open SSH connection to DevLaptop Device, if there's one in the level.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ShowPlayerPath ===<br />
<syntaxhighlight source lang="lua">Debug.ShowPlayerPath(value)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| value || bool<br />
|}<br />
'''Description''': ShowPlayerPath<br />
<br />
'''Returns''': Nothing<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Debug_Lua_API&diff=1606Debug Lua API2021-10-14T09:10:02Z<p>WikiAdmin: Created page with "<!-- This file is auto generated, please don't edit manually! --> = Debug = == Description == Debugging & Testing tools == Functions == === ConnectToDevLaptop === <syntaxhighl..."</p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Debug =<br />
== Description ==<br />
Debugging & Testing tools<br />
== Functions ==<br />
=== ConnectToDevLaptop ===<br />
<syntaxhighlight source lang="lua">Debug.ConnectToDevLaptop()</syntaxhighlight><br />
'''Description''': Open SSH connection to DevLaptop Device, if there's one in the level.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ShowPlayerPath ===<br />
<syntaxhighlight source lang="lua">Debug.ShowPlayerPath(value)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| value || bool<br />
|}<br />
'''Description''': ShowPlayerPath<br />
<br />
'''Returns''': Nothing</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Animator_Lua_API&diff=1605Animator Lua API2021-10-14T09:08:41Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Animator =<br />
== Description ==<br />
Allows the user to set properties of a mission objects Unity animator<br />
The animator allows users to set key keyframe-able animations to control a large amount of components.<br />
See [[https://docs.unity3d.com/Manual/class-Animator.html Unity's documentation]] for more details<br />
== Functions ==<br />
=== SetBool ===<br />
<syntaxhighlight source lang="lua">Animator.SetBool(missionObjectName, parameterName, value)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObjectName || string<br />
|-<br />
| parameterName || string<br />
|-<br />
| value || bool<br />
|}<br />
'''Description''': Sets a bool parameter by name<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetFloat ===<br />
<syntaxhighlight source lang="lua">Animator.SetFloat(missionObjectName, parameterName, value)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObjectName || string<br />
|-<br />
| parameterName || string<br />
|-<br />
| value || number<br />
|}<br />
'''Description''': Sets a float parameter by name<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetInt ===<br />
<syntaxhighlight source lang="lua">Animator.SetInt(missionObjectName, parameterName, value)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObjectName || string<br />
|-<br />
| parameterName || string<br />
|-<br />
| value || number<br />
|}<br />
'''Description''': Sets a int parameter by name<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetTrigger ===<br />
<syntaxhighlight source lang="lua">Animator.SetTrigger(missionObjectName, parameterName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObjectName || string<br />
|-<br />
| parameterName || string<br />
|}<br />
'''Description''': Begins a trigger parameter by name<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetBool ===<br />
<syntaxhighlight source lang="lua">Animator.GetBool(missionObjectName, parameterName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObjectName || string<br />
|-<br />
| parameterName || string<br />
|}<br />
'''Description''': Gets the current value of a bool parameter by name<br />
<br />
'''Returns''': The current bool value of the parameter<br />
<br />
=== GetFloat ===<br />
<syntaxhighlight source lang="lua">Animator.GetFloat(missionObjectName, parameterName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObjectName || string<br />
|-<br />
| parameterName || string<br />
|}<br />
'''Description''': Gets the current value of a float parameter by name<br />
<br />
'''Returns''': The current float value of the parameter<br />
<br />
=== GetInt ===<br />
<syntaxhighlight source lang="lua">Animator.GetInt(missionObjectName, parameterName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObjectName || string<br />
|-<br />
| parameterName || string<br />
|}<br />
'''Description''': Gets the current value of a int parameter by name<br />
<br />
'''Returns''': The current int value of the parameter<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Apps_Lua_API&diff=1604Apps Lua API2021-10-14T09:08:15Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Apps =<br />
== Description ==<br />
The Apps API allows modders to control Apps<br />
== Functions ==<br />
=== RequestAppState ===<br />
<syntaxhighlight source lang="lua">Apps.RequestAppState(appName, newState)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| appName || string<br />
|-<br />
| newState || OffGridApp+AppStates<br />
|}<br />
'''Description''': Ask an App to change it's state<br />
<br />
'''Returns''': Nothing<br />
<br />
== Global Functions ==<br />
=== SetState ===<br />
<syntaxhighlight source lang="lua">SetState(newState)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| newState || OffGridApp+AppStates<br />
|}<br />
'''Description''': Sets the App's state.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== CreateStatusWindow ===<br />
<syntaxhighlight source lang="lua">CreateStatusWindow()</syntaxhighlight><br />
'''Description''': Create the status window<br />
<br />
'''Returns''': Nothing<br />
<br />
=== RemoveStatusWindow ===<br />
<syntaxhighlight source lang="lua">RemoveStatusWindow()</syntaxhighlight><br />
'''Description''': Remove this app's status window<br />
<br />
'''Returns''': Nothing<br />
<br />
=== DisplayStatusWindow ===<br />
<syntaxhighlight source lang="lua">DisplayStatusWindow(enabled)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| enabled || bool<br />
|}<br />
'''Description''': Show or hide the app's status window.<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': Make sure to to create the window first ;)<br />
=== UpdateStatusWindow ===<br />
<syntaxhighlight source lang="lua">UpdateStatusWindow(text)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| text || string<br />
|}<br />
'''Description''': Update the text content of the app's status window<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetStatusIcon ===<br />
<syntaxhighlight source lang="lua">SetStatusIcon(id)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| id || number<br />
|}<br />
'''Description''': Set the icon used for the app's status window<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': ID should match with an image in your app definition's 'statusIcons' table.<br />
=== SetStatusIconColor ===<br />
<syntaxhighlight source lang="lua">SetStatusIconColor(color)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| color || Lua Type<br />
|}<br />
'''Description''': Set the color of the status window icon<br />
<br />
'''Returns''': Nothing<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Color_Lua_API&diff=1603Color Lua API2021-10-14T09:04:29Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Color =<br />
== Description ==<br />
Allows quick access to colours matching the Off Grid colour pallette<br />
== Functions ==<br />
=== RandomColor ===<br />
<syntaxhighlight source lang="lua">Color.RandomColor(alpha)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| alpha || number (optional)<br />
|}<br />
'''Description''': Returns a random color<br />
<br />
'''Returns''': Lua Table<br />
<br />
'''Notes''': Defaults to full alpha, optionally pass in required alpha<br />
=== LessRandomColor ===<br />
<syntaxhighlight source lang="lua">Color.LessRandomColor(hMin, hMax, sMin, sMax, vMin, vMax, aMin, aMax)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| hMin || number<br />
|-<br />
| hMax || number<br />
|-<br />
| sMin || number<br />
|-<br />
| sMax || number<br />
|-<br />
| vMin || number<br />
|-<br />
| vMax || number<br />
|-<br />
| aMin || number<br />
|-<br />
| aMax || number<br />
|}<br />
'''Description''': Returns a random color based on hue, saturation, value, and alpha upper and lower limits<br />
<br />
'''Returns''': Lua Table<br />
<br />
== Variables ==<br />
== Variables ==<br />
=== Clear ===<br />
<syntaxhighlight source lang="lua">Color.Clear</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #ffffff;"| || 1, 1, 1<br />
|-<br />
|}<br />
<br />
=== Black ===<br />
<syntaxhighlight source lang="lua">Color.Black</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #181818;"| || 0.094, 0.094, 0.094<br />
|-<br />
|}<br />
<br />
=== DarkGrey ===<br />
<syntaxhighlight source lang="lua">Color.DarkGrey</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #585858;"| || 0.345, 0.345, 0.345<br />
|-<br />
|}<br />
<br />
=== Grey ===<br />
<syntaxhighlight source lang="lua">Color.Grey</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #B8B8B8;"| || 0.722, 0.722, 0.722<br />
|-<br />
|}<br />
<br />
=== LightGrey ===<br />
<syntaxhighlight source lang="lua">Color.LightGrey</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #D8D8D8;"| || 0.847, 0.847, 0.847<br />
|-<br />
|}<br />
<br />
=== White ===<br />
<syntaxhighlight source lang="lua">Color.White</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #F8F8F8;"| || 0.972, 0.972, 0.972<br />
|-<br />
|}<br />
<br />
=== Red ===<br />
<syntaxhighlight source lang="lua">Color.Red</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #AB4642;"| || 0.671, 0.275, 0.259<br />
|-<br />
|}<br />
<br />
=== Orange ===<br />
<syntaxhighlight source lang="lua">Color.Orange</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #DC9656;"| || 0.863, 0.589, 0.336<br />
|-<br />
|}<br />
<br />
=== Yellow ===<br />
<syntaxhighlight source lang="lua">Color.Yellow</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #F7CA88;"| || 0.969, 0.792, 0.533<br />
|-<br />
|}<br />
<br />
=== Green ===<br />
<syntaxhighlight source lang="lua">Color.Green</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #A1B56C;"| || 0.631, 0.71, 0.424<br />
|-<br />
|}<br />
<br />
=== Cyan ===<br />
<syntaxhighlight source lang="lua">Color.Cyan</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #86C1B9;"| || 0.525, 0.757, 0.725<br />
|-<br />
|}<br />
<br />
=== Blue ===<br />
<syntaxhighlight source lang="lua">Color.Blue</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #7CAFC2;"| || 0.486, 0.686, 0.761<br />
|-<br />
|}<br />
<br />
=== Purple ===<br />
<syntaxhighlight source lang="lua">Color.Purple</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #BA8BAF;"| || 0.729, 0.545, 0.686<br />
|-<br />
|}<br />
<br />
=== Brown ===<br />
<syntaxhighlight source lang="lua">Color.Brown</syntaxhighlight><br />
'''Description''': <br />
{| class=wikitable<br />
! Preview !! RGB<br />
|-<br />
|style="background: #A16946;"| || 0.631, 0.412, 0.275<br />
|-<br />
|}<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Constants&diff=1602Constants2021-10-14T09:03:19Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Constants =<br />
== Enums ==<br />
=== DataType ===<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| DataType.Generic || Any random data. Who knows, could be anything.<br />
|-<br />
| DataType.Text || Plain text or rich text content.<br />
|-<br />
| DataType.SMS || SMS. Don't use this! Legacy stuff.<br />
|-<br />
| DataType.Image || Image file. We'll try to display this for you in the File Viewer.<br />
|-<br />
| DataType.Audio || Audio file. There's no viewer for these yet.<br />
|-<br />
| DataType.Video || Video file. There's no viewer for these yet.<br />
|-<br />
| DataType.Location || Location data. just the coordinates, no much use apart from being a location marker in level<br />
|-<br />
| DataType.Key || PGP key. For encryption, device/lock access etc.<br />
|-<br />
| DataType.UUID || Generic identifier for something.<br />
|}<br />
=== DataValue ===<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| DataValue.Low || <br />
|-<br />
| DataValue.Medium || <br />
|-<br />
| DataValue.High || <br />
|-<br />
| DataValue.Important || <br />
|}<br />
=== MissionObjectType ===<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| MissionObjectType.Trigger || <br />
|-<br />
| MissionObjectType.Interaction || <br />
|-<br />
| MissionObjectType.Spawn || <br />
|-<br />
| MissionObjectType.Hackable || <br />
|-<br />
| MissionObjectType.Generic || <br />
|-<br />
| MissionObjectType.Deprecated || <br />
|-<br />
| MissionObjectType.Timeline || <br />
|}<br />
=== InteractionType ===<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| InteractionType.Grab || <br />
|-<br />
| InteractionType.OpenDoor || <br />
|-<br />
| InteractionType.Scanning || <br />
|}<br />
=== InteractionRequirement ===<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| InteractionRequirement.None || <br />
|-<br />
| InteractionRequirement.Item || <br />
|-<br />
| InteractionRequirement.Data || <br />
|-<br />
| InteractionRequirement.Key || <br />
|}<br />
=== AIReactionType ===<br />
The type of reaction that can affect NPCs<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| AIReactionType.FixAmokDevice || The NPC will attempt to fix the amok device<br />
|}<br />
=== AppState ===<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| AppState.unavailable || The player doesn't have this app yet.<br />
|-<br />
| AppState.disabled || App can't be used at the moment, for example no network connection<br />
|-<br />
| AppState.off || App is not doing anything<br />
|-<br />
| AppState.on || App is switched on and running in the background<br />
|-<br />
| AppState.alert || App displays alert to notify the player about something.<br />
|}<br />
=== AppMenuState ===<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| AppMenuState.any || Allowed in any of configuration<br />
|-<br />
| AppMenuState.never || Never allowed in the selected menu<br />
|-<br />
| AppMenuState.always || Always in the selected menu<br />
|-<br />
| AppMenuState.byDefault || In the selected menu by default<br />
|-<br />
| AppMenuState.target || Will be in the radial menu when its target types are selected<br />
|}<br />
=== TrackingStates ===<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| TrackingStates.off || Not connected to SPECTRUM.<br />
|-<br />
| TrackingStates.tracking || Connected to SPECTRUM.<br />
|-<br />
| TrackingStates.alert || Connected to SPECTRUM, almost out of time.<br />
|-<br />
| TrackingStates.overtime || Connected to SPECTRUM, over time, restricted to minimum range<br />
|-<br />
| TrackingStates.cooldown || Disconnected from SPECTRUM, recovering.<br />
|-<br />
| TrackingStates.unlimited || Unlimited connection. Cheater.<br />
|}<br />
=== NetworkType ===<br />
The type of network, different devices and characters will only connect to certain network types<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| NetworkType.mobile || Mobile network (4G)- This will mainly just contain mobile phone devices<br />
|-<br />
| NetworkType.wifi || Wifi networks will be the most common, and will contain all sorts of devices<br />
|-<br />
| NetworkType.mesh || Mesh networks are mainly set up as part of a storyline<br />
|-<br />
| NetworkType.nfc || <br />
|}<br />
=== TargetType ===<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| TargetType.None || <br />
|-<br />
| TargetType.Data || <br />
|-<br />
| TargetType.Interaction || <br />
|-<br />
| TargetType.Hackable || <br />
|-<br />
| TargetType.Character || <br />
|}<br />
=== NotificationType ===<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| NotificationType.sms || <br />
|-<br />
| NotificationType.generic || Generic notification<br />
|-<br />
| NotificationType.message || Communicatiosn message<br />
|-<br />
| NotificationType.download || Receicing files<br />
|-<br />
| NotificationType.newObjective || <br />
|-<br />
| NotificationType.completedObjective || <br />
|}<br />
=== PopupType ===<br />
{| class="wikitable"<br />
|-<br />
! Usage !! Description<br />
|-<br />
| PopupType.generic || A generic popup<br />
|-<br />
| PopupType.message || A message to the player<br />
|-<br />
| PopupType.warning || A warning to the player<br />
|-<br />
| PopupType.download || Begin a timed download window<br />
|-<br />
| PopupType.progress || Trigger a progress bar<br />
|}<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Conversation_Lua_API&diff=1601Conversation Lua API2021-10-14T09:02:42Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Conversation =<br />
== Description ==<br />
Allows the user to start scripted conversations<br />
== Functions ==<br />
=== StartScript ===<br />
<syntaxhighlight source lang="lua">Conversation.StartScript(path, onCompleteCallback)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| path || string<br />
|-<br />
| onCompleteCallback || Lua Type<br />
|}<br />
'''Description''': Starts a conversation from a name<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': Must be in MissionFolder/Conversations/$path<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Data_Points_Lua_Api&diff=1600Data Points Lua Api2021-10-14T09:02:10Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= DataPoints =<br />
== Description ==<br />
The DataPoints API returns and manage the wanted available data points<br />
== Functions ==<br />
=== GetAllDataPoints ===<br />
<syntaxhighlight source lang="lua">DataPoints.GetAllDataPoints()</syntaxhighlight><br />
'''Description''': Return all the data points that are currently in the level<br />
<br />
'''Returns''': System.Collections.Generic.List`1[DataPoint]<br />
<br />
=== GetObjectDataPoints ===<br />
<syntaxhighlight source lang="lua">DataPoints.GetObjectDataPoints(objectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectName || string<br />
|}<br />
'''Description''': Return all the data points received by an object or a character <br />
<br />
'''Returns''': System.Collections.Generic.List`1[DataPoint]<br />
<br />
=== FilterNetwork ===<br />
<syntaxhighlight source lang="lua">DataPoints.FilterNetwork(dataPoints, networkName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataPoints || System.Collections.Generic.List`1[DataPoint]<br />
|-<br />
| networkName || string<br />
|}<br />
'''Description''': Filter data points with the network name<br />
<br />
'''Returns''': System.Collections.Generic.List`1[DataPoint]<br />
<br />
=== FilterDataType ===<br />
<syntaxhighlight source lang="lua">DataPoints.FilterDataType(dataPoints, dataType)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataPoints || System.Collections.Generic.List`1[DataPoint]<br />
|-<br />
| dataType || string<br />
|}<br />
'''Description''': Filter data points with the data type<br />
<br />
'''Returns''': System.Collections.Generic.List`1[DataPoint]<br />
<br />
=== DeleteDataPoints ===<br />
<syntaxhighlight source lang="lua">DataPoints.DeleteDataPoints(dataPoints)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataPoints || System.Collections.Generic.List`1[DataPoint]<br />
|}<br />
'''Description''': Remove data points from the level<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetData ===<br />
<syntaxhighlight source lang="lua">DataPoints.GetData(dataPoints)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataPoints || System.Collections.Generic.List`1[DataPoint]<br />
|}<br />
'''Description''': Return all the data string from the given data points<br />
<br />
'''Returns''': System.String[]<br />
<br />
=== GetDataPointInfo ===<br />
<syntaxhighlight source lang="lua">DataPoints.GetDataPointInfo(dataPoints)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataPoints || System.Collections.Generic.List`1[DataPoint]<br />
|}<br />
'''Description''': Return all the data info from the given data points<br />
<br />
'''Returns''': System.Collections.Generic.List`1[DataPointInfo]<br />
<br />
=== PlayersInventorySave ===<br />
<syntaxhighlight source lang="lua">DataPoints.PlayersInventorySave(dataFile)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataFile || DataPointInfo<br />
|}<br />
'''Description''': Save data info in the the players inventory<br />
<br />
'''Returns''': Nothing<br />
<br />
=== PlayersInventoryRemove ===<br />
<syntaxhighlight source lang="lua">DataPoints.PlayersInventoryRemove(dataFile)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataFile || DataPointInfo<br />
|}<br />
'''Description''': Save data info in the the players inventory<br />
<br />
'''Returns''': Nothing<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Devices_Lua_API&diff=1598Devices Lua API2021-10-14T09:00:43Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Devices =<br />
== Description ==<br />
The Devices API is used to control the behavior of provided devices in the level kit each of these calls will have a slightly different response to each of these calls.<br />
Please see the devices page for a full break down.<br />
== Functions ==<br />
=== SetPower ===<br />
<syntaxhighlight source lang="lua">Devices.SetPower(deviceName, state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Change the powered on state of the device<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': See provided devices page to see how each device handles this call<br />
=== TogglePower ===<br />
<syntaxhighlight source lang="lua">Devices.TogglePower(deviceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|}<br />
'''Description''': Flip the powered on state of the device<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': See provided devices page to see how each device handles this call<br />
=== GetPower ===<br />
<syntaxhighlight source lang="lua">Devices.GetPower(deviceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|}<br />
'''Description''': Get the powered on state of the device<br />
<br />
'''Returns''': If the device is currently powered on<br />
<br />
=== SetActive ===<br />
<syntaxhighlight source lang="lua">Devices.SetActive(deviceName, state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Change the active state of the device<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': See provided devices page to see how each device handles this call<br />
=== ToggleActive ===<br />
<syntaxhighlight source lang="lua">Devices.ToggleActive(deviceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|}<br />
'''Description''': Flip the active state of the device<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': See provided devices page to see how each device handles this call<br />
=== GetActive ===<br />
<syntaxhighlight source lang="lua">Devices.GetActive(deviceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|}<br />
'''Description''': Get the active state of the device<br />
<br />
'''Returns''': If the device is currently active<br />
<br />
=== RunOnce ===<br />
<syntaxhighlight source lang="lua">Devices.RunOnce(deviceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|}<br />
'''Description''': Trigger a single update of the device<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': See provided devices page to see how each device handles this call<br />
=== SetAmok ===<br />
<syntaxhighlight source lang="lua">Devices.SetAmok(deviceName, state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Begin an 'Amok' state, cause the device to act in an unstable/broken manor<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': See provided devices page to see how each device handles this call<br />
=== ToggleAmok ===<br />
<syntaxhighlight source lang="lua">Devices.ToggleAmok(deviceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|}<br />
'''Description''': Flip the amok state<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': See provided devices page to see how each device handles this call<br />
=== GetAmok ===<br />
<syntaxhighlight source lang="lua">Devices.GetAmok(deviceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|}<br />
'''Description''': Get the 'Amok' state of the device<br />
<br />
'''Returns''': If the device is currently running 'Amok'<br />
<br />
=== SetValue ===<br />
<syntaxhighlight source lang="lua">Devices.SetValue(deviceName, newValue)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| newValue || string<br />
|}<br />
'''Description''': Pass a string value to the device<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': See provided devices page to see how each device handles this call<br />
=== GetValue ===<br />
<syntaxhighlight source lang="lua">Devices.GetValue(deviceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|}<br />
'''Description''': Get the current value of the device<br />
<br />
'''Returns''': The value of the device<br />
<br />
=== GetDataInventory ===<br />
<syntaxhighlight source lang="lua">Devices.GetDataInventory(deviceName, index)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| index || number<br />
|}<br />
'''Description''': Get the description of the data at this index in the DataInventory<br />
<br />
'''Returns''': A description of that piece of data<br />
<br />
=== GetDataPointFromInventory ===<br />
<syntaxhighlight source lang="lua">Devices.GetDataPointFromInventory(deviceName, index)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| index || number<br />
|}<br />
'''Description''': Get a table of useful data from the DataPoint at this index in the DataInventory<br />
<br />
'''Returns''': A description of that piece of data<br />
<br />
=== GetDataInventoryCount ===<br />
<syntaxhighlight source lang="lua">Devices.GetDataInventoryCount(deviceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|}<br />
'''Description''': Get the number of DataPoints in the DataInventory of this device<br />
<br />
'''Returns''': The number of DataPoints<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Doors_Lua_API&diff=1597Doors Lua API2021-10-14T09:00:18Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Doors =<br />
== Description ==<br />
API to control the logic of doors in the mission<br />
== Functions ==<br />
=== SetZoneKeys ===<br />
<syntaxhighlight source lang="lua">Doors.SetZoneKeys(zoneName, keyNames)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| zoneName || string<br />
|-<br />
| keyNames || Lua Table<br />
|}<br />
'''Description''': Sets a key as unlocking a specific zone<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetNetwork ===<br />
<syntaxhighlight source lang="lua">Doors.SetNetwork(networkName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| networkName || string<br />
|}<br />
'''Description''': Sets the name of the network for the door system to use<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetKeyOnDevice ===<br />
<syntaxhighlight source lang="lua">Doors.SetKeyOnDevice(keyName, deviceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| keyName || string<br />
|-<br />
| deviceName || string<br />
|}<br />
'''Description''': Sets a key as the current NFC file on a net device<br />
<br />
'''Returns''': Nothing<br />
<br />
=== AssignKeyToCharacter ===<br />
<syntaxhighlight source lang="lua">Doors.AssignKeyToCharacter(keyName, character)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| keyName || string<br />
|-<br />
| character || Lua Type<br />
|}<br />
'''Description''': Adds a key to a characters inventory and sets it as the characters current NFC data<br />
<br />
'''Returns''': Nothing<br />
<br />
=== Open ===<br />
<syntaxhighlight source lang="lua">Doors.Open(doorID)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| doorID || string<br />
|}<br />
'''Description''': Open the specified door.<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': This call will not open a locked door. If the door is (or could potentially be) locked, call Unlock first to guarantee that it will open<br />
=== Close ===<br />
<syntaxhighlight source lang="lua">Doors.Close(doorID)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| doorID || string<br />
|}<br />
'''Description''': close the specified door.<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': This will close the door regardless of is someone is in the way. Please contact Semaeopus Health & Safety if this is a problem.<br />
=== Unlock ===<br />
<syntaxhighlight source lang="lua">Doors.Unlock(doorID)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| doorID || string<br />
|}<br />
'''Description''': Unlock the specified door<br />
<br />
'''Returns''': Nothing<br />
<br />
=== Lock ===<br />
<syntaxhighlight source lang="lua">Doors.Lock(doorID)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| doorID || string<br />
|}<br />
'''Description''': Lock the specified door<br />
<br />
'''Returns''': Nothing<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Flutter_Lua_API&diff=1596Flutter Lua API2021-10-14T08:59:56Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Flutter =<br />
== Description ==<br />
The Flutter API allows modders to send Flutter messages<br />
== Tables ==<br />
=== FlutterMessage ===<br />
<br />
<br />
'''Table definition'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type !! Default !! Optional !! Description<br />
|-<br />
| message || string || || No || Content string for the message<br />
|-<br />
| hashTags || table || || No || custom hash tags for the message<br />
|-<br />
| tags || table || || No || metadata tags<br />
|-<br />
| metadata || table || || No || metadata values<br />
|}<br />
== Functions ==<br />
=== SendMessageFromCharacter ===<br />
<syntaxhighlight source lang="lua">Flutter.SendMessageFromCharacter(message, internalName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| message || Lua Type<br />
|-<br />
| internalName || string<br />
|}<br />
'''Description''': Sends a custom FlutterMessage from NPC in current level<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SendRandomMessageFromCharacter ===<br />
<syntaxhighlight source lang="lua">Flutter.SendRandomMessageFromCharacter(internalName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|}<br />
'''Description''': Sends a generated Flutter message from NPC in current level<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SendRandomMessage ===<br />
<syntaxhighlight source lang="lua">Flutter.SendRandomMessage()</syntaxhighlight><br />
'''Description''': Sends a generated Flutter message from generated random character<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetFlutterEnabled ===<br />
<syntaxhighlight source lang="lua">Flutter.SetFlutterEnabled(enabled)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| enabled || bool<br />
|}<br />
'''Description''': Set Flutter app enabled or disabled.<br />
<br />
'''Returns''': Nothing<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=GameProgress_Lua_API&diff=1595GameProgress Lua API2021-10-14T08:59:28Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= GameProgress =<br />
== Description ==<br />
The GameProgress api allows the modder to control and query the state of the players game progress<br />
== Functions ==<br />
=== GetValue ===<br />
<syntaxhighlight source lang="lua">GameProgress.GetValue(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Returns a value from the game progress by key, empty string if it doesn't exist<br />
<br />
'''Returns''': string<br />
<br />
=== SetValue ===<br />
<syntaxhighlight source lang="lua">GameProgress.SetValue(key, value, overwrite)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| value || string<br />
|-<br />
| overwrite || bool (optional)<br />
|}<br />
'''Description''': Sets a value in the game progress<br />
<br />
'''Returns''': Nothing<br />
<br />
=== HasKey ===<br />
<syntaxhighlight source lang="lua">GameProgress.HasKey(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Does the specified key exist?<br />
<br />
'''Returns''': bool<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Mission_Lua_API&diff=1594Mission Lua API2021-10-14T08:57:19Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Mission =<br />
== Description ==<br />
The Mission API controls the flow of the mission, this includes starting and stopping the mission, and dealing with objectives and other mission-related information<br />
== Functions ==<br />
=== MissionStarted ===<br />
<syntaxhighlight source lang="lua">Mission.MissionStarted()</syntaxhighlight><br />
'''Description''': This should be called once the initial state of the mission has been set<br />
It will cause the game to begin gameplay<br />
<br />
'''Returns''': Nothing<br />
<br />
=== MissionCompleted ===<br />
<syntaxhighlight source lang="lua">Mission.MissionCompleted()</syntaxhighlight><br />
'''Description''': This should be called when the final objective of the mission has been completed<br />
It will trigger the game to fade to black and return to the main menu<br />
<br />
'''Returns''': Nothing<br />
<br />
=== MissionFailed ===<br />
<syntaxhighlight source lang="lua">Mission.MissionFailed()</syntaxhighlight><br />
'''Description''': This function will fail the current mission, as if Joe had been tazed (or similar).<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SpawnCharacter ===<br />
<syntaxhighlight source lang="lua">Mission.SpawnCharacter(internalName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|}<br />
'''Description''': Spawn a registered character in the game<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ObjectiveIsActive ===<br />
<syntaxhighlight source lang="lua">Mission.ObjectiveIsActive(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Returns true if objective has been started but not completed yet.<br />
<br />
'''Returns''': bool<br />
<br />
=== ObjectiveIsCompleted ===<br />
<syntaxhighlight source lang="lua">Mission.ObjectiveIsCompleted(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Returns true if objective has been completed.<br />
<br />
'''Returns''': bool<br />
<br />
=== StartObjective ===<br />
<syntaxhighlight source lang="lua">Mission.StartObjective(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Start the provided objective<br />
<br />
'''Returns''': Nothing<br />
<br />
=== CompleteObjective ===<br />
<syntaxhighlight source lang="lua">Mission.CompleteObjective(objectiveName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectiveName || string<br />
|}<br />
'''Description''': Complete the provided objective<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetCurrentObjective ===<br />
<syntaxhighlight source lang="lua">Mission.GetCurrentObjective()</syntaxhighlight><br />
'''Description''': Get the name of the current objective<br />
<br />
'''Returns''': string<br />
<br />
=== TriggerAutoSave ===<br />
<syntaxhighlight source lang="lua">Mission.TriggerAutoSave()</syntaxhighlight><br />
'''Description''': Triggers an autosave, only if the game is current in a mission<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetMissionObjectInteractionRequirement ===<br />
<syntaxhighlight source lang="lua">Mission.SetMissionObjectInteractionRequirement(name, requirement)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| name || string<br />
|-<br />
| requirement || number<br />
|}<br />
'''Description''': Sets if a MissionObject should request the player to sleect an item or data file before interaction<br />
<br />
'''Returns''': Nothing<br />
<br />
=== DevicesConnected ===<br />
<syntaxhighlight source lang="lua">Mission.DevicesConnected()</syntaxhighlight><br />
'''Description''': Call this AFTER all Devices have been connected to their networks. This allows DataPoints to be processed correctly.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetPlayerControlled ===<br />
<syntaxhighlight source lang="lua">Mission.SetPlayerControlled(tf)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| tf || bool<br />
|}<br />
'''Description''': Set whether the player is currently in control (or not). Used for cutscenes, and other things that we haven't thought of yet.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetBool ===<br />
<syntaxhighlight source lang="lua">Mission.GetBool(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a boolean value of a key in mission Lua script. Returns false if key doesn't exist.<br />
<br />
'''Returns''': bool<br />
<br />
=== SetBool ===<br />
<syntaxhighlight source lang="lua">Mission.SetBool(key, newBool)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newBool || bool<br />
|}<br />
'''Description''': Sets a boolean value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetString ===<br />
<syntaxhighlight source lang="lua">Mission.GetString(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a string value of a key in mission Lua script. Returns empty if key doesn't exist.<br />
<br />
'''Returns''': string<br />
<br />
=== SetString ===<br />
<syntaxhighlight source lang="lua">Mission.SetString(key, newString)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newString || string<br />
|}<br />
'''Description''': Sets a string value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetNumber ===<br />
<syntaxhighlight source lang="lua">Mission.GetNumber(key)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|}<br />
'''Description''': Get a numeric value of a key in mission Lua script. Returns 0 if key doesn't exist.<br />
<br />
'''Returns''': number<br />
<br />
=== SetNumber ===<br />
<syntaxhighlight source lang="lua">Mission.SetNumber(key, newValue)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| key || string<br />
|-<br />
| newValue || number<br />
|}<br />
'''Description''': Sets a numeric value of a key in mission Lua script.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== StartVibrationEvent ===<br />
<syntaxhighlight source lang="lua">Mission.StartVibrationEvent(objectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectName || string<br />
|}<br />
'''Description''': Start a mission object vibration event<br />
<br />
'''Returns''': Nothing<br />
<br />
=== StopVibrationEvent ===<br />
<syntaxhighlight source lang="lua">Mission.StopVibrationEvent(objectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| objectName || string<br />
|}<br />
'''Description''': Stop a mission object vibration event<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SpawnWanderNPCs ===<br />
<syntaxhighlight source lang="lua">Mission.SpawnWanderNPCs(spawnPoint, spawnNum, characterPrefabs, headProps, colorTextures, metalTextures, gestures)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| spawnPoint || Lua Type<br />
|-<br />
| spawnNum || number<br />
|-<br />
| characterPrefabs || Lua Type<br />
|-<br />
| headProps || Lua Type<br />
|-<br />
| colorTextures || Lua Type<br />
|-<br />
| metalTextures || Lua Type<br />
|-<br />
| gestures || Lua Type<br />
|}<br />
'''Description''': Spawns various Wander NPCs in a randomized way<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': All the parameters, expect the spawnNum, can be a string or a table of strings. For the head props and the the gestures the string "None" can be used to have the possibility to not have any head props and gestures, respectively, in the wander NPCs. Also for the color textures and metal textures the string "Default" can be used for the character's default textures.<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Network_Lua_API&diff=1593Network Lua API2021-10-14T08:56:41Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Network =<br />
== Description ==<br />
Network API handles netwroks, connecting and disconnecting devices, and sending data between characters and devices.<br />
== Functions ==<br />
=== CreateDataPoint ===<br />
<syntaxhighlight source lang="lua">Network.CreateDataPoint(internalName, dataTable, locationObject, characterName, networkName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|-<br />
| dataTable || Lua Table<br />
|-<br />
| locationObject || string<br />
|-<br />
| characterName || string<br />
|-<br />
| networkName || string<br />
|}<br />
'''Description''': Create a DataPoint in a specific place, from a specific character, with pre filled in data.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ConnectToNetwork ===<br />
<syntaxhighlight source lang="lua">Network.ConnectToNetwork(connector, targetNetwork, accessKey)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| connector || Lua Type<br />
|-<br />
| targetNetwork || string<br />
|-<br />
| accessKey || string<br />
|}<br />
'''Description''': Connect a device (or a table of devices) to a specified network<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SendData ===<br />
<syntaxhighlight source lang="lua">Network.SendData(internalName, dataTable, sender, receiver)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|-<br />
| dataTable || Lua Table<br />
|-<br />
| sender || Lua Type<br />
|-<br />
| receiver || Lua Type<br />
|}<br />
'''Description''': Send a data file from one device to another<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': Much like _ConnectToNetwork_ both _senderName_ and _receiverName_ can be character names (assuming those characters have net devices)<br />
=== SetNetDeviceNFCData ===<br />
<syntaxhighlight source lang="lua">Network.SetNetDeviceNFCData(internalName, dataTable, deviceTable)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|-<br />
| dataTable || Lua Table<br />
|-<br />
| deviceTable || Lua Table<br />
|}<br />
'''Description''': Sets NFC data on a mission object<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetNetDeviceNFC ===<br />
<syntaxhighlight source lang="lua">Network.SetNetDeviceNFC(deviceName, enabled)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| enabled || bool<br />
|}<br />
'''Description''': Set if a mission object supports NFCAddNetDevice<br />
<br />
'''Returns''': Nothing<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Noise_Lua_API&diff=1592Noise Lua API2021-10-14T08:56:02Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Noise =<br />
== Description ==<br />
The Noise system provides a method of producing sounds that the AI can be aware of and respond to.<br />
<br />
<br />
== Tables ==<br />
=== Noise ===<br />
Table representing a noise<br />
<br />
'''Table definition'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type !! Default !! Optional !! Description<br />
|-<br />
| soundName || string || || No || The name of the sound event<br />
|-<br />
| attraction || number || || No || How much attraction this noise will cause (range 0-1)<br />
|-<br />
| volume || number || 1.0 || Yes || The volume of the sound event (range (0-1)<br />
|-<br />
| duration || number || 0.0 || Yes || How long does this noise last? 0 will loop the sound and require it to be explicitly ended<br />
|-<br />
| trusted || boolean || false || Yes || Whether this noise is trusted by AIs<br />
|-<br />
| offEvent || string || || Yes || Sound to emit when the source is silenced <br />
|}<br />
== Functions ==<br />
=== Emit ===<br />
<syntaxhighlight source lang="lua">Noise.Emit(deviceName, noiseTable)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| noiseTable || Lua Table<br />
|}<br />
'''Description''': Emits a noise from deviceName with attributes in noiseTable<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': Attraction value is required, volume is optional (default 1.0), as is duration (defaults to 0, which loops the sound).<br />
=== Silence ===<br />
<syntaxhighlight source lang="lua">Noise.Silence(deviceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|}<br />
'''Description''': Silences the noise<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': Silences the noise, principally from an AI perspective. Will attempt to stop the audio if this noise was set up with an end event.<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Particles_Lua_API&diff=1591Particles Lua API2021-10-14T08:55:32Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Particles =<br />
== Description ==<br />
<br />
The Particles API allows modders to trigger behaviours on the particle systems attached to mission objects and their children.<br />
For more information see [https://docs.unity3d.com/Manual/class-ParticleSystem.html Unity's documentation] on Particle Systems<br />
== Functions ==<br />
=== Play ===<br />
<syntaxhighlight source lang="lua">Particles.Play(deviceName, searchChildren)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| searchChildren || bool (optional)<br />
|}<br />
'''Description''': Sets the particle system into play mode and beings emitting<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': Sets the particle systems into play mode and enables emitting (if it has been disabled).<br />
If the particle system has been paused, then this resumes playing from the previous time.<br />
If the particle system has stopped, then the system starts from time 0, and, if it is relevant, the startDelay is applied.<br />
=== Pause ===<br />
<syntaxhighlight source lang="lua">Particles.Pause(deviceName, searchChildren)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| searchChildren || bool (optional)<br />
|}<br />
'''Description''': Pauses playing the particle system.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== Stop ===<br />
<syntaxhighlight source lang="lua">Particles.Stop(deviceName, searchChildren)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| searchChildren || bool (optional)<br />
|}<br />
'''Description''': Stops playing the particle system.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== Toggle ===<br />
<syntaxhighlight source lang="lua">Particles.Toggle(deviceName, searchChildren)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| searchChildren || bool (optional)<br />
|}<br />
'''Description''': Toggles the particle system playing<br />
<br />
'''Returns''': Nothing<br />
<br />
=== Emit ===<br />
<syntaxhighlight source lang="lua">Particles.Emit(deviceName, count, searchChildren)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| count || number<br />
|-<br />
| searchChildren || bool (optional)<br />
|}<br />
'''Description''': Emit _count_ particles immediately.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== IsPlaying ===<br />
<syntaxhighlight source lang="lua">Particles.IsPlaying(deviceName, searchChildren)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| deviceName || string<br />
|-<br />
| searchChildren || bool (optional)<br />
|}<br />
'''Description''': Is the particle system playing right now?<br />
<br />
'''Returns''': bool<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Player_Lua_API&diff=1590Player Lua API2021-10-14T08:54:10Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Player =<br />
== Description ==<br />
The Player API handles querying and setting the state of the player<br />
== Functions ==<br />
=== AddItemToInventory ===<br />
<syntaxhighlight source lang="lua">Player.AddItemToInventory(itemName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| itemName || string<br />
|}<br />
'''Description''': Adds an item to the players inventory (silently, wihtout a notification. Also check Mission.SendData() ).<br />
<br />
'''Returns''': Nothing<br />
<br />
=== RemoveItemFromInventory ===<br />
<syntaxhighlight source lang="lua">Player.RemoveItemFromInventory(itemName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| itemName || string<br />
|}<br />
'''Description''': Removes an item to the players inventory<br />
<br />
'''Returns''': Nothing<br />
<br />
=== HasItem ===<br />
<syntaxhighlight source lang="lua">Player.HasItem(itemName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| itemName || string<br />
|}<br />
'''Description''': Is an item in the players inventory<br />
<br />
'''Returns''': true if the item is in the players inventory, else false<br />
<br />
=== ClearInventory ===<br />
<syntaxhighlight source lang="lua">Player.ClearInventory()</syntaxhighlight><br />
'''Description''': Removes all items from the players inventory<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetPlayerNFCData ===<br />
<syntaxhighlight source lang="lua">Player.SetPlayerNFCData(internalName, dataTable)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|-<br />
| dataTable || Lua Table<br />
|}<br />
'''Description''': Sets the NFC data on the players phone<br />
<br />
'''Returns''': Nothing<br />
<br />
=== AddDataFile ===<br />
<syntaxhighlight source lang="lua">Player.AddDataFile(internalName, dataTable)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|-<br />
| dataTable || Lua Table<br />
|}<br />
'''Description''': Adds a data file directly to the players data inventory without a sender<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ClearDataInventory ===<br />
<syntaxhighlight source lang="lua">Player.ClearDataInventory()</syntaxhighlight><br />
'''Description''': Removes all data files from the players data inventory<br />
<br />
'''Returns''': Nothing<br />
<br />
=== HasDataFile ===<br />
<syntaxhighlight source lang="lua">Player.HasDataFile(dataFileName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataFileName || string<br />
|}<br />
'''Description''': Does the player have a data file with this name?<br />
<br />
'''Returns''': bool<br />
<br />
=== HasEncryptedFile ===<br />
<syntaxhighlight source lang="lua">Player.HasEncryptedFile(dataFileName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataFileName || string<br />
|}<br />
'''Description''': Does the player have a file with the name _dataFileName_ and is it encrypted?<br />
<br />
'''Returns''': bool<br />
<br />
=== HasDecryptedFile ===<br />
<syntaxhighlight source lang="lua">Player.HasDecryptedFile(dataFileName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataFileName || string<br />
|}<br />
'''Description''': Does the player have a file with the name _dataFileName_ and is it unencrypted?<br />
<br />
'''Returns''': bool<br />
<br />
=== GetDataString ===<br />
<syntaxhighlight source lang="lua">Player.GetDataString(internalName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|}<br />
'''Description''': Returns the data string of a file in the players inventory, takes in the internal name of the file<br />
<br />
'''Returns''': string<br />
<br />
=== SetDataString ===<br />
<syntaxhighlight source lang="lua">Player.SetDataString(internalName, newString)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|-<br />
| newString || string<br />
|}<br />
'''Description''': Sets the data string of a data file in the players data inventory<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetAllDataFileNames ===<br />
<syntaxhighlight source lang="lua">Player.GetAllDataFileNames()</syntaxhighlight><br />
'''Description''': Return internalNames of all files in the players data inventory<br />
<br />
'''Returns''': System.String[]<br />
<br />
=== GetAllDataFiles ===<br />
<syntaxhighlight source lang="lua">Player.GetAllDataFiles()</syntaxhighlight><br />
'''Description''': Return a table of all files in the players data inventory<br />
<br />
'''Returns''': Lua Type<br />
<br />
=== SendData ===<br />
<syntaxhighlight source lang="lua">Player.SendData(internalName, receiver)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || Lua Type<br />
|-<br />
| receiver || Lua Type<br />
|}<br />
'''Description''': Send a file from player's inventory to receiver<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetSocialProfileSize ===<br />
<syntaxhighlight source lang="lua">Player.GetSocialProfileSize(internalName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|}<br />
'''Description''': Returns the size of the background profile player has collected about a character<br />
<br />
'''Returns''': number<br />
<br />
=== SocialProfileContainsTag ===<br />
<syntaxhighlight source lang="lua">Player.SocialProfileContainsTag(internalName, tag)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|-<br />
| tag || string<br />
|}<br />
'''Description''': Returns true if social profile contains any character data with specified tag<br />
<br />
'''Returns''': bool<br />
<br />
=== SocialProfileContainsTagData ===<br />
<syntaxhighlight source lang="lua">Player.SocialProfileContainsTagData(internalName, tag, data)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|-<br />
| tag || string<br />
|-<br />
| data || string<br />
|}<br />
'''Description''': Returns true if social profile contains character data matching both tag and data<br />
<br />
'''Returns''': bool<br />
<br />
=== SocialProfileContainsData ===<br />
<syntaxhighlight source lang="lua">Player.SocialProfileContainsData(internalName, data)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|-<br />
| data || string<br />
|}<br />
'''Description''': Returns true if social profile contains specified character data (regardless of it's tag)<br />
<br />
'''Returns''': bool<br />
<br />
=== AddSocialProfileInformation ===<br />
<syntaxhighlight source lang="lua">Player.AddSocialProfileInformation(internalName, tag, data)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|-<br />
| tag || string<br />
|-<br />
| data || string<br />
|}<br />
'''Description''': Add new background data about a character to SocialInventory<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetSocialProfileInformation ===<br />
<syntaxhighlight source lang="lua">Player.GetSocialProfileInformation(internalName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| internalName || string<br />
|}<br />
'''Description''': Get all known background data about a character from player's SocialInventory<br />
<br />
'''Returns''': Lua Type<br />
<br />
=== GetPlayerTrackingState ===<br />
<syntaxhighlight source lang="lua">Player.GetPlayerTrackingState()</syntaxhighlight><br />
'''Description''': Get current tracking state from Player's phone.<br />
<br />
'''Returns''': PlayerPhone+TrackingStates<br />
<br />
=== GetNetPointsCount ===<br />
<syntaxhighlight source lang="lua">Player.GetNetPointsCount()</syntaxhighlight><br />
'''Description''': Get current amount of NetPoints the player has.<br />
<br />
'''Returns''': number<br />
<br />
=== SetNetPointsCount ===<br />
<syntaxhighlight source lang="lua">Player.SetNetPointsCount(count)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| count || number<br />
|}<br />
'''Description''': Set player's NetPoints count.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== AddNetPoints ===<br />
<syntaxhighlight source lang="lua">Player.AddNetPoints(count)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| count || number<br />
|}<br />
'''Description''': Add more NetPoints to player<br />
<br />
'''Returns''': Nothing<br />
<br />
=== RemoveNetPoints ===<br />
<syntaxhighlight source lang="lua">Player.RemoveNetPoints(count)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| count || number<br />
|}<br />
'''Description''': Take NetPOintsa from player.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== GetName ===<br />
<syntaxhighlight source lang="lua">Player.GetName()</syntaxhighlight><br />
'''Description''': Get the Player's internalName<br />
<br />
'''Returns''': string<br />
<br />
=== GetLightLevel ===<br />
<syntaxhighlight source lang="lua">Player.GetLightLevel()</syntaxhighlight><br />
'''Description''': Get the light level around the player<br />
<br />
'''Returns''': number<br />
<br />
=== SetAlwaysRagdoll ===<br />
<syntaxhighlight source lang="lua">Player.SetAlwaysRagdoll(state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Player character aways ragdolls on death<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetInvisible ===<br />
<syntaxhighlight source lang="lua">Player.SetInvisible(state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Player character is invisible<br />
<br />
'''Returns''': Nothing<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Scheduler_Lua_API&diff=1589Scheduler Lua API2021-10-14T08:53:46Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Scheduler =<br />
== Description ==<br />
The Scheduler api allows users to Schedule callback events based on specific time values<br />
== Functions ==<br />
=== CallInSecsReal ===<br />
<syntaxhighlight source lang="lua">Scheduler.CallInSecsReal(func, timeInSecs)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| func || Lua Type<br />
|-<br />
| timeInSecs || number<br />
|}<br />
'''Description''': Schedule a lua function to be called in timeInSecs real time<br />
<br />
'''Returns''': The id of the scheduled event<br />
<br />
=== CallInSecs ===<br />
<syntaxhighlight source lang="lua">Scheduler.CallInSecs(func, timeInSecs)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| func || Lua Type<br />
|-<br />
| timeInSecs || number<br />
|}<br />
'''Description''': Schedule a lua function to be called in timeInSecs scaled time<br />
<br />
'''Returns''': The id of the scheduled event<br />
<br />
=== CallAtTime ===<br />
<syntaxhighlight source lang="lua">Scheduler.CallAtTime(func, dateTimeString)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| func || Lua Type<br />
|-<br />
| dateTimeString || string<br />
|}<br />
'''Description''': Schedule a lua function to be called at specific time (defined by a date/time string)<br />
<br />
'''Returns''': The id of the scheduled event<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Sound_Lua_API&diff=1588Sound Lua API2021-10-14T08:51:18Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Sound =<br />
== Description ==<br />
The sound API allows modders to trigger sound events in game<br />
== Functions ==<br />
=== TriggerEvent ===<br />
<syntaxhighlight source lang="lua">Sound.TriggerEvent(eventName, sourceName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| eventName || string<br />
|-<br />
| sourceName || string<br />
|}<br />
'''Description''': Triggers a sound even with name ''eventName''<br />
<br />
'''Returns''': The event id, this can be used in other sound API functions (see [[#StopAudio|StopAudio]])<br />
<br />
'''Notes''': If _sourceName_ is not provided the sound will be played as a '2D' event<br />
=== SetRTPC ===<br />
<syntaxhighlight source lang="lua">Sound.SetRTPC(sourceName, RTPCName, value)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| sourceName || string<br />
|-<br />
| RTPCName || string<br />
|-<br />
| value || number<br />
|}<br />
'''Description''': Set Real-Time Parameter Curve for an audio event playign on this object<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': You need to have started an audio event on this object before tryign to use SetRTPC, and that event must have RTPC control for something.<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Spectrum_Lua_API&diff=1587Spectrum Lua API2021-10-14T08:50:04Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Spectrum =<br />
== Description ==<br />
The Spectrum api allows the manipulation of data points within the game world<br />
== Functions ==<br />
=== DeleteDataPoint ===<br />
<syntaxhighlight source lang="lua">Spectrum.DeleteDataPoint(dataPoint)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataPoint || DataPoint<br />
|}<br />
'''Description''': Deletes the passed in data point<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SaveDataPoint ===<br />
<syntaxhighlight source lang="lua">Spectrum.SaveDataPoint(dataPoint)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| dataPoint || DataPoint<br />
|}<br />
'''Description''': Saves currently targeted data point to the players data inventory<br />
<br />
'''Returns''': Nothing<br />
<br />
=== FilterClear ===<br />
<syntaxhighlight source lang="lua">Spectrum.FilterClear()</syntaxhighlight><br />
'''Description''': Clear the Filter<br />
<br />
'''Returns''': Nothing<br />
<br />
=== FilterType (int, bool) ===<br />
<syntaxhighlight source lang="lua">Spectrum.FilterType (int, bool)(t, on)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| t || number<br />
|-<br />
| on || bool<br />
|}<br />
'''Description''': Filter which types of Data Spectrum shows<br />
<br />
'''Returns''': Nothing<br />
<br />
=== FilterCreator ===<br />
<syntaxhighlight source lang="lua">Spectrum.FilterCreator(creator)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| creator || DataPoint<br />
|}<br />
'''Description''': Shows only Data from the Creator of the specified DataPoint. Pass nil to clear<br />
<br />
'''Returns''': Nothing<br />
<br />
=== FilterNetwork ===<br />
<syntaxhighlight source lang="lua">Spectrum.FilterNetwork(network)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| network || DataPoint<br />
|}<br />
'''Description''': Shows only Data from the Network of the specified DataPoint. Pass nil to clear<br />
<br />
'''Returns''': Nothing<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=Timeline_Lua_API&diff=1586Timeline Lua API2021-10-14T08:48:41Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= Timeline =<br />
== Description ==<br />
Allows controlling Timelines on MissionObjects<br />
== Functions ==<br />
=== Play ===<br />
<syntaxhighlight source lang="lua">Timeline.Play(missionObjectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObjectName || string<br />
|}<br />
'''Description''': Starts the timeline<br />
<br />
'''Returns''': Nothing<br />
<br />
=== Pause ===<br />
<syntaxhighlight source lang="lua">Timeline.Pause(missionObjectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObjectName || string<br />
|}<br />
'''Description''': Pauses the timeline<br />
<br />
'''Returns''': Nothing<br />
<br />
=== Stop ===<br />
<syntaxhighlight source lang="lua">Timeline.Stop(missionObjectName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObjectName || string<br />
|}<br />
'''Description''': Stops the timeline<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetDynamicCameraTarget ===<br />
<syntaxhighlight source lang="lua">Timeline.SetDynamicCameraTarget(missionObjectName, targetName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObjectName || string<br />
|-<br />
| targetName || string<br />
|}<br />
'''Description''': Sets the look at & follow target for all dynamically targeted cameras in the timeline<br />
<br />
'''Returns''': Nothing<br />
<br />
=== PauseAI ===<br />
<syntaxhighlight source lang="lua">Timeline.PauseAI(characterName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|}<br />
'''Description''': Pauses an AI agent.<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': Bit of a hack for Timeline use, this might get removed later on!<br />
=== UnpauseAI ===<br />
<syntaxhighlight source lang="lua">Timeline.UnpauseAI(characterName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|}<br />
'''Description''': Unpauses an AI agent, resuming standard behaviour.<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': Bit of a hack for Timeline use, this might get removed later on!<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=UI_Lua_API&diff=1585UI Lua API2021-10-14T08:47:26Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= UI =<br />
== Description ==<br />
The UI API allows modders to control UI elements of the game<br />
== Functions ==<br />
=== ToggleClock ===<br />
<syntaxhighlight source lang="lua">UI.ToggleClock(state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Toggles the clock hud element<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ToggleWeather ===<br />
<syntaxhighlight source lang="lua">UI.ToggleWeather(state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Toggles the weather hud element<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetDataViewState ===<br />
<syntaxhighlight source lang="lua">UI.SetDataViewState(state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Sets the state of the data view<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': This can be used for dramatic effect when certain story elements trigger<br />
=== OpenRemoteConnection ===<br />
<syntaxhighlight source lang="lua">UI.OpenRemoteConnection(missionObject)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| missionObject || MissionObject<br />
|}<br />
'''Description''': Opens SSH connection to currently targeted device<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetRadialScanState ===<br />
<syntaxhighlight source lang="lua">UI.SetRadialScanState(shouldScan)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| shouldScan || bool<br />
|}<br />
'''Description''': Control if the radial menu should be scanning for targets<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ToggleUIMarkers ===<br />
<syntaxhighlight source lang="lua">UI.ToggleUIMarkers(state)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| state || bool<br />
|}<br />
'''Description''': Show/Hide UI markers for hackable and interactable objects near the player.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ShowHint ===<br />
<syntaxhighlight source lang="lua">UI.ShowHint(message, timeout)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| message || string<br />
|-<br />
| timeout || number (optional)<br />
|}<br />
'''Description''': Displays a hint message. Multiple messages will stack on screen but don't go too crazy...<br />
<br />
'''Returns''': Nothing<br />
<br />
<span style="color:#009000">'''Tip'''</span>: These should mainly be used as additioanl help to the player, outside of the context of the game world. <br />
=== ShowModalMessage ===<br />
<syntaxhighlight source lang="lua">UI.ShowModalMessage(luaMessage)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| luaMessage || Lua Table<br />
|}<br />
'''Description''': Displays a modal, multiple calls to this will cause a stack of modals the user can click through<br />
<br />
'''Returns''': Nothing<br />
<br />
<span style="color:#009000">'''Tip'''</span>: These can be helpful to display story context and tutorialise hacking puzzles<br />
=== ShowPopUp ===<br />
<syntaxhighlight source lang="lua">UI.ShowPopUp(type, header, message, timeout)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| type || OffGridPopup+PopupTypes<br />
|-<br />
| header || string<br />
|-<br />
| message || string<br />
|-<br />
| timeout || number (optional)<br />
|}<br />
'''Description''': Displays a small pop up in the centre of the screen<br />
<br />
'''Returns''': Nothing<br />
<br />
=== ShowNotification ===<br />
<syntaxhighlight source lang="lua">UI.ShowNotification(type, header, message, timeout)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| type || OffGridNotification+NotificationTypes<br />
|-<br />
| header || string<br />
|-<br />
| message || string<br />
|-<br />
| timeout || number (optional)<br />
|}<br />
'''Description''': Displays a notification pop up in the top right corner of the screen<br />
<br />
'''Returns''': Nothing<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=AI_Lua_API&diff=1584AI Lua API2021-10-14T08:46:03Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= AI =<br />
== Description ==<br />
API to control the logic of AI in the mission<br />
== Functions ==<br />
=== Pause ===<br />
<syntaxhighlight source lang="lua">AI.Pause(characterName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|}<br />
'''Description''': Pauses the agent, and stops it from doing anything.<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': The AI will be hidden from networks, meaning it will no longer interact with devices or send/receive messages.<br />
=== Unpause ===<br />
<syntaxhighlight source lang="lua">AI.Unpause(characterName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|}<br />
'''Description''': Unpauses a hidden agent, resuming standard behaviour.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== IsPaused ===<br />
<syntaxhighlight source lang="lua">AI.IsPaused(characterName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|}<br />
'''Description''': Returns a bool based on if a character is currently paused or not<br />
<br />
'''Returns''': bool<br />
<br />
=== AddGoal ===<br />
<syntaxhighlight source lang="lua">AI.AddGoal(characterName, goal)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| goal || Lua Table<br />
|}<br />
'''Description''': Adds a defined goal<br />
<br />
'''Returns''': Nothing<br />
<br />
=== RemoveGoal ===<br />
<syntaxhighlight source lang="lua">AI.RemoveGoal(characterName, goalName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| goalName || string<br />
|}<br />
'''Description''': Removes any defined goals<br />
<br />
'''Returns''': Nothing<br />
<br />
=== AddTemporaryGoal ===<br />
<syntaxhighlight source lang="lua">AI.AddTemporaryGoal(characterName, goal)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| goal || Lua Table<br />
|}<br />
'''Description''': Adds a goal, taking top priority, to the named AI<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': This goal will be removed from the AI once complete. If it isn't achievable it will be removed immediately.<br />
=== GetNPCStat ===<br />
<syntaxhighlight source lang="lua">AI.GetNPCStat(characterName, statName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| statName || string<br />
|}<br />
'''Description''': Returns current value of NPC's stat<br />
<br />
'''Returns''': number<br />
<br />
'''Notes''': This is only intended for debugging purposes, and not recommended in any actual game scripts.<br />
=== SetNPCStat ===<br />
<syntaxhighlight source lang="lua">AI.SetNPCStat(characterName, statName, newValue)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| statName || string<br />
|-<br />
| newValue || number<br />
|}<br />
'''Description''': Sets an NPC's AI stat to absolute value<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': This is only intended for debugging purposes, and not recommended in any actual game scripts.<br />
=== AlterNPCStat ===<br />
<syntaxhighlight source lang="lua">AI.AlterNPCStat(characterName, statName, statDelta)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| statName || string<br />
|-<br />
| statDelta || number<br />
|}<br />
'''Description''': Alters an NPC's AI stat by relative value<br />
<br />
'''Returns''': Nothing<br />
<br />
=== AlterNPCWorldState ===<br />
<syntaxhighlight source lang="lua">AI.AlterNPCWorldState(characterName, state, value)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| state || string<br />
|-<br />
| value || Lua Type<br />
|}<br />
'''Description''': Change the World State of an NPC.<br />
<br />
'''Returns''': Nothing<br />
<br />
=== FavourInterest ===<br />
<syntaxhighlight source lang="lua">AI.FavourInterest(characterName, device, permanent)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| device || string<br />
|-<br />
| permanent || bool<br />
|}<br />
'''Description''': Reduce the cost of an AI using a particular Interest<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': If permanent is false, the cost will revert to normal the next time this is successfully used<br />
=== AvoidInterest ===<br />
<syntaxhighlight source lang="lua">AI.AvoidInterest(characterName, device, permanent)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| device || string<br />
|-<br />
| permanent || bool<br />
|}<br />
'''Description''': Increase the cost of an AI using a particular Interest<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': If permanent is false, the cost will revert to normal the next time this is successfully used<br />
=== ChangeSubject ===<br />
<syntaxhighlight source lang="lua">AI.ChangeSubject(characterName, subject, value)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| subject || string<br />
|-<br />
| value || string<br />
|}<br />
'''Description''': Set the value of a particular subject, enabling Actions with Personality requirements<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': value can be null or empty (to unset/reset the subject)<br />
=== ReactTo ===<br />
<syntaxhighlight source lang="lua">AI.ReactTo(characterName, subject, value)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| subject || string<br />
|-<br />
| value || string<br />
|}<br />
'''Description''': Much like ChangeSubject, but instead of enabling Actions, this will alter stats within the Agent, modifying its WorldState (and as a result, enabling Actions)<br />
<br />
'''Returns''': Nothing<br />
<br />
'''Notes''': value can be null or empty (to unset/reset the subject)<br />
=== CreateReactable ===<br />
<syntaxhighlight source lang="lua">AI.CreateReactable(actionType, attraction, targetObject)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| actionType || AIReaction+Type<br />
|-<br />
| attraction || number<br />
|-<br />
| targetObject || MissionObject<br />
|}<br />
'''Description''': Create a new distraction that AI can pick up on<br />
<br />
'''Returns''': Nothing<br />
<br />
=== SetNPCFavouredComputer ===<br />
<syntaxhighlight source lang="lua">AI.SetNPCFavouredComputer(characterName, computer)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| computer || MissionObject<br />
|}<br />
'''Description''': Set NPC's computer, this will be used for a variety of actions<br />
<br />
'''Returns''': Nothing<br />
<br />
=== AddAction ===<br />
<syntaxhighlight source lang="lua">AI.AddAction(characterName, action)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| action || Lua Table<br />
|}<br />
'''Description''': Adds an action that can be used immediately<br />
<br />
'''Returns''': Nothing<br />
<br />
=== RemoveAction ===<br />
<syntaxhighlight source lang="lua">AI.RemoveAction(characterName, actionName)</syntaxhighlight><br />
'''Expected parameter types'''<br />
{| class="wikitable"<br />
|-<br />
! Name !! Type<br />
|-<br />
| characterName || string<br />
|-<br />
| actionName || string<br />
|}<br />
'''Description''': Removes any action<br />
<br />
'''Returns''': Nothing<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdminhttp://wiki.offgridthegame.com/index.php?title=AI_Gestures&diff=1583AI Gestures2021-10-14T08:45:12Z<p>WikiAdmin: </p>
<hr />
<div><!-- This file is auto generated, please don't edit manually! --><br />
= AI Gestures =<br />
== Description ==<br />
The enums on this page can be used by AI agent definitions to play an animation when an action is taken. These enums map directly on to states in the character animator, which is shared by all characters. Make sure you type the name exactly as it appears here, it's case sensitive.<br />
== Fields ==<br />
=== LookingAround ===<br />
Looks from side to side.<br />
<br />
=== LookAtPhone ===<br />
Retrieves phone from pocket, has a look at it, then puts it away.<br />
<br />
=== StandingTypingCalm ===<br />
Typing from a standing position, in a fairly normal manner.<br />
<br />
=== StandingTypingStealth ===<br />
Typing from a standing position, in a sneaky manner.<br />
<br />
=== StandingFilingCabinetSearch ===<br />
Searches a filing cabinet.<br />
<br />
=== StandingLookAtHipHeight ===<br />
Not sure.<br />
<br />
=== StandingAmokDeviceHit ===<br />
Hits a device that's in front of the character.<br />
<br />
=== UseSodaMachine ===<br />
Press button, retrieve soda, drink it, discard can.<br />
<br />
=== UseSodaMachineHacked ===<br />
Press button, get hit by can, keel over.<br />
<br />
=== UseCoffeeMachine ===<br />
Press button, retrieve coffee cup, drink.<br />
<br />
=== UseCoffeeMachineHacked ===<br />
Press button, get hit by steam, panic and writhe in pain.<br />
<br />
=== DanceHipHop ===<br />
Hip hop dancing.<br />
<br />
=== DanceGuitar ===<br />
Air guitar!<br />
<br />
=== DanceSalsa ===<br />
Strictly come Off Grid.<br />
<br />
=== Yawn ===<br />
Yawn, with arms outstretched.<br />
<br />
=== StandingTalkingCalmly ===<br />
Standing talking calmly with hands near waist.<br />
<br />
=== StandingTalkingAngrily ===<br />
Standing talking angrily with hand gestures.<br />
<br />
=== StandingUseTazer ===<br />
Standing raises right arm in front.<br />
<br />
=== StandingPhoneMalfunctioning ===<br />
Gets phone out, swipes frantically, puts phone away and looks around.<br />
<br />
=== StandingLookingHunched ===<br />
Standing, almost crouches down and peers around.<br />
<br />
=== StandingLookingSideToSide ===<br />
Standing, looks side to side.<br />
<br />
=== StandingHitFace ===<br />
Standing, quickly brings hands to face.<br />
<br />
=== PickupLow ===<br />
Standing pick up from ankle hight.<br />
<br />
=== PickupMed ===<br />
Standing pick up from waist hight.<br />
<br />
=== PickupHigh ===<br />
Standing pick up from head hight.<br />
<br />
=== LockedDoor ===<br />
Standing, puts hand on handle, struggles.<br />
<br />
=== WashingDryingHands ===<br />
Standing, hands out in front rubbing together.<br />
<br />
=== WaitingHandsOnHips ===<br />
Standing with hands on hips.<br />
<br />
=== WashFace ===<br />
Standing washing face.<br />
<br />
=== UseSnackMachine ===<br />
<br />
<br />
=== UseSnackMachineHacked ===<br />
<br />
<br />
=== AttackPlayerDefault ===<br />
Attack player without weapons<br />
<br />
=== Sitting ===<br />
Sitting on a chair<br />
<br />
<br />
<br />
This file is auto generated, please don't edit manually!<br />
<br />
'''Docs last hacked together on''': 13/10/2021 15:55<br />
[[Category:Modding]][[Category:LuaAPI]]</div>WikiAdmin