[Mod] EmpyrionCommandCrate v2.9.2 (EmpyrionSpotGuard addon)

Discussion in 'Empyrion API' started by Exacute, Jul 23, 2018.

  1. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    CommandCrate - A SpotGuard addon

    CommandCrate (CC for short), is a mod that effectively works as a plugin container.
    The purpose of CC is to offer an 'easier to use' API, that doesn't require any real coding on your part.
    It allows you to create your own custom 'commands' in YAML, that can do all sorts of things to one or several players.


    Basic
    The basic concept of CC is a Chat Command, ie. an user types something, and something happens.


    [​IMG]
    -Every command starts with a prefix. (-2)
    This can be '.' or '/' or '!' or 's! '. This is non-configurable.
    Immediatly after the prefix, the next position starts.
    -A command may be required to use the 'namespace' as defined in config.yaml. By default, it is optional to use. (-1)
    (If optional, you could both type '!tp <player>' and '!cc tp <player>'. If required, only '!cc tp <player>' would work)
    -After the prefix, the actual command starts.
    You are able to look for a keyword (Such as 'tp' or 'tpto'), or you can decide to look for a 'thing', for instance items (id/name), a clients ID, the steamID of a person, an entityid (currently only playerIDs are supported, but it is planned when possible), a playerID (the entityid of a player), a playfield, a faction, an integer, a string, a boolean (true/false), a POI (ship or base) or a playername.

    You are able to set parameters as optional, to require additional conditions to be met (such as have to share origin with the executing player), that it have to be a 'whitelisted word' or 'not a blacklisted word'. You are additionally able to specify, wether you want to use what was typed as a target (if faction for instance, will run the command on all the online members of that faction) and if there should be a defaultvalue for it, if nothing is specified.

    You may repeat the pattern of (0) as many times as you want. (In the example, it first looks if there is typed 'tp' or 'tpto' .. Then it looks if there is typed a player, or a playfield (effectively). In the example, the playfield is not used as a target, so it will not try to execute it on the players on the playfield.

    You are additionally able to specify, what the data is 'temporarily stored as'. You can use this later in the command, such as to check what was typed in a certain spot, etc.
    Code:
      CommandEntry: #react to !tp, !tpto
      - i: 0
        key: tpto
        type: key
      - i: 0
        key: tp
        type: key
      - i: 1 #At 2nd place, expect entityid, playerid, playername, steamid or clientid
        type: entityid
        #accept: sameorigin
      - i: 1
        type: playerid
        #accept: sameorigin
      - i: 1
        type: playername
        #accept: sameorigin
      - i: 1
        type: steam
        #accept: sameorigin
      - i: 1
        type: client
        #accept: sameorigin
      - i: 1
        type: playfield
        key: playfield 
    Note, that all commands are *NOT* case-sensitive (Everything gets lower-cased), to make it easier to use. Ie, 'tp' and 'TP' and 'tP' and 'Tp' all resolves to 'tp'.
    In your CommandEntry, you should lowercase it aswell.

    Every command have this basic logic:
    -An executing player
    -And a target player
    If multiple targets are specified (such as in the case, where you may use a faction as the target), it will run the command x-times. One for each member of the faction as the target.
    Note, that if no target is used, it will use the player executing, as the target.

    In order to actually be executed, a few additional checks will occur.
    -Is the user allowed to execute the command?
    You are able to check, if the user meets a number of criterias. You can mix these criterias as you please.
    You are able to check for the following
    * Their Administrator rank
    * Their playtime
    * Their Reputation
    * Their VIP status
    For each of these, you are able to specify
    *If it needs to be exactly a number
    *Above a number
    *Below a number
    [​IMG]

    -Is the command on cooldown?
    You are able to limit how often a player can perform a command.
    If the command were successfull, it will put it on cooldown. When the cooldown have passed, the player will be able to execute the command again.
    You can specify the cooldown as
    *Minutes that must pass real-time
    *Minutes that must pass, while the player is ingame
    *The next 'of day' (For instance, 'reset every Monday')
    *The next 'of date' (For instance, 'resets every 5th of the month')

    You are able to mix these in the same fashion, as the requirements, meaning that you can provide several options.
    You are additionally able to use the requirements, to further specify the cooldowns
    [​IMG]
    As with the Requirements, you can freely mix the requirements, aswell as the cooldowns. The TP command is pretty straightforward, but you can make some very specific ones, such as for anyone with high reputation or VIP, it will reset on even days, and for the rest, on odd days, etc.

    -Can the player pay the price?
    You are able to specify a cost, for using the command.
    The cost can be
    *Reputation (Will subtract reputation on use)
    *Credits (Will take this amount of credits from the player)
    *Items (Will take these items from the player)
    You are able to introduce an element of randomness, to the items.
    You can specify a number of entries, and how many entries should be picked. For instance, you can specify 10 entries, and specify it should pick 2.
    For each entry, you are able to specify multiple options, and an amount.
    For instance, you could specify, that the user must have 2 IronOre or IronIngot.
    In addition, you are able to weight the chance, that this entry will be picked.

    [​IMG]
    In the example, it will pick 1 or 2 entries.
    It is allowed to pick the same entry multiple times.
    So the player may be required to pay 2 CopperIngots, 1 CopperIngot and 10 CrushedStone, CopperIngot and 2 IronOre/IronIngot, 4 IronOre/IronIngots, 2 IronOre/IronIngot and 10 Crushedstone or 20 CrushedStone.

    The odds are in favor of it being 4 IronIngots/IronOre however.

    -The TP command additionally makes use of 'TargetAccept' - Meaning, that before the player gets charged, or anything happens, the target must accept the action.

    Actions
    If the player was allowed to execute the command, this is what will happen.
    You can specify multiple actions, delay them, and more.
    You can even execute other commands from here.

    -Each action starts of with a logic section. You can for instance check if the player is within a specific set of coordinates, or on a playfield, or have a certain item, etc. See the StringReplace section for all on this.
    If the Logic section was successfull, it will execute the actual Action.

    You can for instance give the player or the target some items, push some messages to them, execute console/telnet commands, spawn buildings, Store data, etc.
    This section also allows for the same randomness, as was used when picking the cost of executing the command.

    You will have to read the actual documentation, to see all the possibilities of what you can do here ;)

    Advanced
    In addition to the basic usuage, you can have commands triggered from other sources, than the player typing them.
    This can for instance be, from a players death, a core being placed, or some other event. See section on 'Hooks'. This will likely be expanded over time aswell.
    You can also trigger commands through the provided API, where you can, if configured, do things from anywhere (Via a HTTP GET).
    You are also able to make a command run every <interval> on each player, and make it executed when they come online, etc.

    You are able to limit how many times a command can be used in total, aswell as requiring 'charges' to use a command. How you give the charges are up to you - You can start them with '10 charges' for instance, or none. You can edit the amount of charges for a command through manually editing 'lastused.yaml' (Requires server restart!), or through ingame commands, or API calls.

    Please note, that the above only really touches on what you can do. You have to dig into the documentation, to fully utilise the mod.

    You, among other things, have access to a lot of 'replacing'. These are stuff in brackets, that will be replaced by something. For instance, you have access to {PlayerName} and {TargetName}, which will resolve to the players name, or targets name. This only scratches on this, and many replacements can also directly affect something, such as modify bagpack slots, do math, etc.
    See the section on 'StringReplace'

    Sharing
    CC is built around the concept, that you can easily share your creations with others.
    You can utilise folders/files to get organised, and keep the overview.
    [​IMG]
    (The folders 'default' and 'utility' comes with the mod, and provides some commands, that you can take inspiration from, or straight up use. It is recommended, that you do review then however, as they may not be desirably for your server)
    You can easily import others creations, by dropping them into the /additional/ folder (Remember to unzip/unrar/.. them, so that you have the .yaml, and optionally .csv file)
    CSV files are used for Localization. While it is not yet possible to use anything but 'english', the framework is there for it.
    YAML files, are where you put your entries (/commands).

    For your convenience, you can uncomment a file, or an entire folder, by typing '#' infront of its name.
    Uncommenting means that it is ignored upon importing.

    You can import / update files without restarting the server, by command '.cc rld cnf'.

    Additional remarks
    Any user can type '.cc' ingame. This will produce a list of all commands accessible to the user, aswell as display those, that they don't have access to, for instance if they require a VIP status, more playtime or higher reputation.
    Admin commands are hidden from normal users, for convenience.
    You are able to hide commands, or make them 'non-user-executable' (So that they can only be executed via a Hook for instance) etc.
    [​IMG]

    By default (Unless disabled for the command), the user is able to type '!help <commandname>'
    This will generate a window, with relevant information, such as the requirements, cost, etc.
    It will not display what it actually does, but you are able to add your own text to the helpentry, if you want, to supplement the auto-generated information.
    [​IMG]

    As things can happen, where the server doesn't start, or stop properly, there's a chance that the lastused.yaml, and/or permadata.yaml file gets corrupted. This should not happen normally, but with everything else, if you interrupt something while it is reading to a file, the results can be less-than-desirable.
    Therefore, CC features a builtin backup for these files, where it by default will save them every server start & server stop. You should normally not have to make use of these backups, but they are provided, in case you need them.
    You can disable this in the config file, if you don't want it.

    Requirements
    As the title suggests, this is a SpotGuard addon. This means, that you must have SpotGuard running on your server, in order to use this.
    SpotGuard supplies a lot of the information that this mod uses, such as their playtime, VIP status, reputation, etc.
    You must for the same reason configure the reference to SpotGuards files properly, in the config file.

    Standards
    This mod uses the following file-standards:
    (If several mods are using the same standard, you are able to configure the paths, so that they can share the same file)
    https://empyrion.gamepedia.com/GameAPI_standards_filestd_specialslist


    Download
    This mod is released as-is.
    You will be able to access newer versions of this mod, if there should come later updates to it.

    In order to download it, please use the following link
    https://www.paypal.com/donate?hosted_button_id=FHKMHFPCU8RKL

    Or simple scan the QR Code
    CommandCrate QR Code.png
    The amount will go to further development of this, and other tools/mods.
    Please save the link, from the page you are redirected to upon successfull transaction.
    The link is always to the latest version.


    Installation
    As with any other mod, installation is simple
    -Download the mod

    -Create a folder named 'Mods' in <yourserverpath>/Content
    -Extract the downloaded files into this folder, so that you have <yourserverpath>/Content/Mods/CommandCrate
    --The folder 'CommandCrate' should contain at the very least: "CommandCrate.dll", "config.yaml".
    --The files "Localization.csv", "lastused.yaml" and "permadata.yaml"
    -Configure the configuration file as you desire. Read the inline comments, for further help on this.

    -Optionally, Remove, edit, or add new commands.
    -Launch the server as normal.
    You can always re-configure things and/or change the files, while the game is running. Just use the ingame-commands specified to reload the list(s).
    After installation, you just launch the server as normal. This is a one-time process, not counting changes you decide to make.

    License
    You are not allowed to edit this mod.
    You are not allowed to redistribute this mod, or any modified version of it.
    You are not allowed to share the download link to this mod.
    You are allowed, and encouraged, create new, or to edit the supplied .yaml and .csv files, to tailor it to your needs.
    You are allowed, and encouraged, to share the .yaml and .csv files with others, to make it easier to get started.
    You are allowed to sell services from this mod (Such as charges, or indirectly through giving them VIP status in SpotGuard, etc)

    Please always keep in mind, the current 'pay to win' stance from Eleon!
    This tool is offering you several ways you can provide incentatives for your users, to donate, vote, behave, etc. It does not hold your hand, as to not break the current rules put in place. Be sure to follow them, for your own sake.

    (Rules can be found at: http://empyriongame.com/server-monetisation-rules/)
     
    #1
    Last edited by a moderator: Nov 2, 2020
  2. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    Changelog
    v1.21
    *Fixed Eleon fuckup that rendered all commands unusuable
    *Changed default config.yaml, to use the new foldername (_A10), to make it easier to install again.

    v1.2
    *The main part of commands will now run in a seperate thread, to not slow the game itself down, in case of complex commands.
    *Fixed issue with for-loop functions, where their localized variables wouldn't properly write things to the outputs (this was affecting the default repview)
    *Fixed case where localized variables would not always get properly written ('global', rather than 'local'). (Please note, that this means everything with {local0=somevalue} or {local_test=somevalue} will be ignored. (local<number> and local_<text>) in stringvalue replacements-variable saving)
    *Updated yamls repview, currencyview, as they had small flaws in their handling of displaying the 'current value'
    *Changed frequency of when 'factions' and 'POIlist' gets updated from 30 minutes, to every 1 minute. DSL seems to not always load all on playfield, so to give {EntityInfo} a better chance of properly giving data, 1 minute seems more appropriate.
    *Added new yaml 'corerewards' to /Extensions/, made by Robomikel. Showcases some functionality with nesting & hooks, aswell as provide a good template to build 'rewards for blowing stuff up' ontop. This one triggers on alien core destructions, rewards a sum of gold, and can only be used once daily.
    *When spawning stuff via the item 'interface' (Spawning structure/poi/etc), you may now in addition to 'player', 'target' and 'alien', use a specific number, referencing a player/faction directly. Note, that the mod *DOES NOT* check if the faction exist / etc. Please note, that you must specify id aswell as factiongroup. See 1.13.21 for documentation.
    *When a core is added/removed, it will now additionally trigger updating the internal 'POIlist'.
    *For ease of use, added more 'types' to 1.13.25 in documentation, aswell as support for keywords.
    *Fixed issue where, if no rep/currency was present for a player, a blank would be created, when querying for their reputation/currency.
    *Fixed issue, where for section of actions would execute, regardless of the conditionals being true. New behaviour: For-loops will only execute, if their corresponding conditionals block returns true.
    *Updated replist and currencylist to have a default, if no entries was found.
    *Added 'MaxValue' to valutahelper : Default is -99999 (no maximum). If set, will limit the total amount of this rep/currency to this number.
    *Added 'Category' to valutahelper: Default is 'unsorted'. If set, will sort under this.
    *Added alphabetical sorting when displaying valuta. It will now sort category (alphabetically), and then the results for each category, will further be sorted, by their names.
    *Fixed issue where valutas would not 'valutaresolve' string replace, if the value was negative.
    *Added categories to visual output for 'curlist' and 'replist' yaml entries.
    *Tweaked how 'permadata' is saved: By default, it will still use 'permadata.yaml', or however it's named. But now, it will support using a new tag, 'File', under 'StoreData'. Please see 1.13.3.3 for documentation. This should not break anything current, but will allow you to store data at a specific file, rather than having all output to 'permadata.yaml'. By default, it will still output to this file, unless anything else is specified. Note that all previous save/references related to permadata should still work.
    *Tweaked how you reference 'permadata': If you are worried for collisions, when querying for data, and have it saved in another file, than 'permadata.yaml', you may do {filename:reference}, rather than just {reference}. If saved in "bounty", and your key is "status", you can either do {status} or {bounty:status}. If you have a reference for 'status' in multiple files, and reference it as {status}, it will be first-found used. Note, that this behaviour is also the case, when using delete replace, or interacting with the set, by those replacements (ie. filename:reference as param). Keep in mind, that filename is filename without .yaml (ie, if filename is bounty.yaml, you really specify 'bounty')
    *reworked conditionals: Now each conditional supports complex logic, rather than just a simple x?y statement. (ie you can do 1==1&&(2==3||!false)) , etc. Please report issues, if you encounter any!). All current conditionals should still work, and it still support xbracketx. Please refer to 'Using conditions: (logic)' for full documentation on what you can do.
    *Improved backup function: Now a folder will be created in the Backup directory, with the name "backup-dd-mm-yyyy_HH-MM-SS_" 'Start' (on start), and 'Stop' (on stop). This folder will contain the files it previously backed up, but with their original name (such as 'lastused.yaml', rather than 'lastused.yaml.backup-04-10-2018_17-46-46_Start.yaml')
    *Added new directory 'permadata' to backup (As it is effectively an extension of the permadata.yaml file)
    *Added valutahelper.yaml to backup aswell, as it might be changed during runtime.
    *Added new config option, PermaDataDirectory. By default it is 'permadata'.
    *Added new hook, ValutaChanged. Will trigger whenever a valuta is updated (Reputation/currency/however you use it). Variables 'amount' : How much it changed by, 'valuta' : What was changed, 'oldval' : What it used to be, 'newval' : What it is now.
    *Added replace {DestroyEntity(id)} : Will remove the targeted entity. (See docu)
    *Added replace {RenameEntity([>id<][>new name<])} : Will rename the targeted entity (See docu)
    *Added replace {SetEntityOwnership([>id<][>faction<])} : Will change the ownership of the target. Does not work for entities. (see docu - It's a friendlier version of the console command 'faction entity', that supports names, abbrevations and ids, and doesn't care about casing).
    *Slightly optimised how 'replace to variable' is done, so that it doesn't run both local references, and permadata, if local is enough.
    *Added replace {POIsInRange(range)} : Will get the names of all POIs within range (meters)
    *Added replace {POIsInRange_id(range)} : Will get the id of all POIs within range (meters)
    *Added replace {POIsInRange_target(range)} : Will get the names of all POIs within range (meters), based on targets position
    *Added replace {POIsInRange_target_id(range)} : Will get the id of all POIs within range (meters), based on targets position
    *Added replace {ClosestPOI(amount)} : Will get the names of n-closest POIs
    *Added replace {ClosestPOI_id(amount)} : Will get the IDs of n-closest POIs
    *Added replace {ClosestPOI_target(amount)} : Will get the names of n-closest POIs, based on targets position
    *Added replace {ClosestPOI_target_id(amount)} : Will get the IDs of n-closest POIs, based on targets position
    *Tweaked the replace function to be more 'willing to stop trying', if there's no hits. Might improve performance some.
    *Added to log debug info, if an entry was skipped importing, due to already existing (For normal .yaml files)
    *Added importer for .valuta.yaml files - If found in the /additional/ folder, These will be injected into the main valutahelper.yaml file.
    *Added importer for .epb files - If found in the /additional/ folder, a copy will be made, and placed in <serverroot>/Content/Prefabs, so that the spawning system can make use of them. (The API requires the blueprint files to be located in this folder, in order to be spawnable. If you are interested in spawning structures, please see the section regarding 'ItemDeliveryMethod').
    *Added new tag, ItemROT, 1.13.28. This can be used to specify the rotation of what is spawned.
    *Fixed issue where roundup & rounddown replaces wouldn't accept negative numbers.
    *Tweaked behaviour of math replace: If something is just a number, like {1}, {123}, {-123} etc, it will also be caught by the math-engine, and turned into the number.
    *Added new custom math-modifiers: # and ¤. These extends what it was (If 2#2, it will do 2+2 (4). If -2#2, it will do -2-2 (-4). ¤ is the inverse, ie. 2¤2, would be 2-2 (0), -2¤2 would be -2+2 (0). You can do -2#-2, which is effectively the same as -2¤2)
    *Fixed issue where math wouldn't operate in the 'proper direction' (It would read right to left, rather than right to left).
    *Added 'absolute' (abs), 'acos' (inverse cosinus), 'asin' (inverse sinus), 'tan' (tangent), 'atan' (inverse tangent) to math.
    *Fixed issue with using 'alien', 'predator' (etc), with SetEntityOwnership
    *Added support for 'alien', 'predator', 'alien', 'prey', 'public', 'private', 'target_public', 'target_private', 'player_public', 'player_private' to ItemFaction
    *Added demo, 'FortificationDemo', in the new pvp pack, in file 'basesdemo.yaml', showcasing use of blueprints shipped with mods, that are used, and spawned in a formation.
    *Fixed issue where 'connect' hook would not fire
    *Added hook for 'disconnect'. NOTE please! read the documentation. This hook will NOT always fire (requires another player online), and it will not fire as the disconnecting player (as that player no longer exist). So make sure to capture {thisplayer} and/or {thissteam}. You can really only use this hook for manipulating some variables, and it's not too reliable for obvious reasons. But. It's there.
    *Tweaked behaviour for epb importing: If blueprint already exist by that name in prefab folder, it will *overwrite it* with whatever was found.
    *Added support for 'subdirectories' inside /Prefabs/ for blueprints. You still just provide 'ItemDeliveryMethod' with a bp name (optionally with a .epb extension), and it will search for it. First it will search the /CCImported/ folder and below. If found, it will take this. Otherwise, it will search back from /Prefabs/ and down, and attempt to find it here. If it weren't found, it will assume you meant to spawn an NPC (NOTE! Game does currently not support this, but framework for it is in place)
    *Tweaked behaviour for epb importing: Blueprints imported will now be placed in a folder called /CCImported/. It will be created if it doesn't exist (Inside /Prefabs/).
    *Added support for using {} for ItemDeliveryMethod.
    *Added FortificationDemo_large to 'basesdemo.yaml' in 'pvp' as a demo for even crazier setpieces. This demo spawns a 5 by 5 unit. It have corners, and middle of walls have a chance to be 1 of 3 pieces. It can have between 1-4 openings. It also comes with defensive structures in each corner.
    *Added import of .data.yaml files. Please see documentation for these. They work differently than other imported files! The purpose is to provide some easy-to-transfer default-data, if you so desire (Can for instance be used as a configuration file for your command).
    *Included 'valutahelper.yaml' to being created if it doesn't already exist in mod files.
    *Fixed issue with death-hooks (playerdeath, playerpvpdeath, playerpvpkill), where the pvp ones would always trigger, and fail, because the game now includes 'entities' in 'who killed the person'. Should now behave properly. Addtionally added new {entityinvolved}. to 'playerdeath' hook, if you are interested in the id of the entity involved, if any. Also added {operatingentity} - If whatever killed the player is operating an entity (player flying a HV for instance), will list the entityid here.
    *Fixed issue, where an infinite loop would occur, if requiring more things to be executed (ExecuteCommand), than options provided, and requiring unique entries for each. This will now execute what was possible, log an error, and then stop trying (Continue to next instruction).
    *Added new tag, 'AttemptToRunEveryIntervalSkipLogin', to allow usuage of '', without doing it the first time the player logs in. Default is false (do it also when player first signs in).
    *Fixed debug line for ecf-file loading, not actually displaying the filename of the file being loaded (Minor, debug-related only)
    *Added replace to 'playerdeath' and 'playerpvpdeath' hooks : {secondssincelastdeath}, will resolve to how many seconds since the player have last died, or NA.
    *Added replace {PlayerSecondsSinceLastDeath} and {TargetSecondsSinceLastDeath}. Will Resolve to how long it have been since the Player, or the Target have died in seconds, or 'NA' if player havn't died this session.
    *Fixed issue, where replace {MoveEntity} wouldn't trigger on playfields with spaces in name (such as 'Andromeda Galaxy'). Additionally, if some rotation-params were specified, but not all, rest will now properly default to '0'
    *Fixed issue with 'ExecuteDelay', where local variables wouldn't be passed on to it. This should now behave more properly.
    *Fixed issue where nesting to other commands would (rarely) fail.
    *Fixed issue, where when using {MoveEntity} and specifying playfields, it would be lowercased always (And hence the game would make it fail). It should now respect playfields aswell.
    *Added support for decimal numbers in 'ExecuteDelay' (0.5 will be half a second, etc)
    *Fixed issue, where {EntityInfo} would resolve to garbage for pos & rot, for bases that have been moved since the server start (Game bug!). The fix skips any structure that have pos (0,0,0) or rot (0,0,0) for updating.. This means, that the data might not be accurate, if the structure have been moved by 'outside forces' (Not by this mod). It's the best I can think of as a workaround however.
    *Mod now updates its internal position data for structures, after moving them, in addition to the 30s intervals, to attempt to keep the data as up-to-data as possible, without hogging absurd amount of ressources.
    *Added replaces' {PlayerRotX}, {PlayerRotY}, {PlayerRotZ}, {TargetRotX}, {TargetRotY}, {TargetRotZ}, that can be used to get the executing players or targets rotation.
    *Tweaked the default virtualbackpack to make use of 'File' and 'filerefernce' for variables.
    *Fixed issue where _id , _target for POIsinrange wouldn't output unless insanely lucky.
    *Migrated from .net3.5 to "Unity 3.5 .net full base class libraries"
    *Updated dependencies dlls to a10
    *Updated chatcommands to new format (Currently no direct benefit)
    *Fixed issue with SG/CC bridge logging being corrupted due to Eleon changing default time & ip formats

    v1.12
    *When using 'ItemCustomName', it will now support string replacements (For instance using {PlayerName})
    *Fixed an issue with a watcher not behaving properly, and spawning childs unintentionally.
    *Fixed minor conflict with some events conflicting on teleport, and watchdog. Not really any impact, but a little neatness cleanup.
    *Fixed issue where the cost (credits) would not be subtracted from the player on execution.
    *Changed default config settings, SpecialsFile and SpotGuardConfigFile, to use the default foldername of 'SpotGuard' (If you directly extracted the files, the folder would be called SpotGuard_A8). This is to make installing more intuitive, although if using another foldername for SG, you will still have to rename these variables.

    v1.11
    *Fixed minor issue where 'Version' was 'version' in yaml file.
    *Fixed issue with users not being properly un-registered, due to some new parameters.
    *Wrong localization file was included in v1.1. Added proper one.

    v1.1
    *Added new 'AdminRank' to config file. This can be used to limit who can use admin commands, by their admin-level.
    *Added loca string for Main_Error_AdminDenied
    *Fixed ingame-message for minutes left, if command on cooldown, being limited to max 60, where it can be more. (visual)
    *Edited 'url' in 'ExternalCall' to 'CCurl', to not conflict with services, that might take POST of 'url'. IF YOU ARE USING 'ExternalCall' please EDIT THE 'url' param to 'CCurl'
    *Added 'CCreturn' to 'ExternalCall'. This will allow for commands to be executed, with the returned data from a call, as a param.
    *Mildly improved replace mechanism in regard to player, target and faction strings. (Small overall performance)
    *Added new 'Contains', {Contains(<>)}, that will return true or false, if it was contained in the string. Previously how 'IndexOf' worked.
    *Changed behaviour of 'IndexOf' to return a number. -1 if not in the string, otherwise the index of which the phrase searched for, was in the string. (As expected with 'IndexOf')
    *Updated demo-commands making use of these.
    *If a player is not member of a faction, {PlayerFactionResolved}, {TargetFactionResolved} will now resolve to 'No Faction' and {PlayerFactionResolvedAbbrev}, {TargetFactionResolvedAbbrev} will resolve to 'None'
    *Added updater for 'all structures' and 'All factions', to run every 30 minutes. (Updates all known-factions and known-structures)
    *Players factions will now be updated, when they change it. (Data-wise for this mod)
    *New hook for players changing factions, 'FactionChange', which can tell old and new factionID
    *Added native 'min' and 'max' to the type:int. And new localization entries, Main_Error_Expected_Min and Main_Error_Expected_Max.
    *New hook for players buying an item from a trader, 'ItemSold'.
    *Added new replace, {Remove([index][amount])}.
    *Added new replace, setAdd, setRemove, setContains, setIndex. Please see documentation. These are for working with sets (something split by something else, for instance a CSV)
    *Added new replace, {DeletePerm([<1>])}. This can be used to *delete* permanent data.
    *Added new replace, {ChargesLeft} and {AmountUsed} to get stats on usuage of this command (from executing players POV)
    *Added new config entry, 'ValutaFile'. Default 'valuta.yaml'
    *Added new file 'valuta.yaml'
    *Added new file to backups
    *Added new replace, ValutaGet, ValutaSet, ValutaAdd. Please see documentation.
    *Added new langstrings, Main_Error_NotExactCustom, Main_Error_TooMuchCustom, Main_Error_TooLittleCustom.
    *Added valuta as condition for Requirements. Please see 1.8.13.
    *Added modulus to math: You can now use '%' aswell.
    *Added replace, FactionsOnPlayfield, to get all IDs of factions inhibiting a playfield.
    *Tweaked replace, FactionMembers, to accept '0', and return all factionless in this case.
    *Fixed issue with 'NestToEntry' in 'forloops' lowering text, and hence not properly doing replacements.
    *Fixed issue with math function outputting non-numbers (such as xxxE-8). Should now always output decimals.
    *Added 'powerof' to math. (A^B) '^'
    *Fixed multiple issue with maths' sqrt not working.
    *Fixed issue with paranthesis not qualifying for math-regex.
    *Added 'sin' and 'cos' to math. (sin(10) cos(10) for instance)
    *Added 'pi' as a math keyword. (π)
    *Added replace for {pi} to resolve to pi's first 10.
    *Added new replace, setSize, to get the size of a given set.
    *Added default values to help command.
    *Added MoveEntity, to move entities around (player + structure + ..)
    *Added new 'utility', 'scatterplayers'. Allows to scatter players on a playfield
    *changed replace, rand, to now accept negative numbers aswell.
    *Fixed issue with math where it would not properly handling negative inputs (-1 for instance), if start of thread.
    *Fixed issue with math where it would not handle +-, --, -+. (These now respect proper math rules)
    *Fixed issue with some types not using 'defaultvalue', such as 'faction' and 'playfield'.
    *Changed type:factions to use their ID for reference, rather than the input.
    *Added support for custom 'dedicated.yaml' versions (ie. that file with other filename(s)), in loading seed + savegame (used for some tag-searching). This will happen automatically, if one such file is found, will take precedence over dedicated.yaml's content.
    *Added new 'utility', 'scatterplayers > tpa'. Allows to teleport all players to the player, or to a playfield. Can additionally be only all members of a faction (Note, A8.2: Please do not use it to teleport to another playfield, it will break your player, and will put you on another playfield on relog (most likely))
    *Added new replace, EntityInfo, allowing to get information on structures & players. See docu.
    *CC now creates a 'ghostplayer' untill SG have detected the player (incase CC discovers it before SG). This should help with 'player not found' errors on first-join, and should allow for successfull execution of the related commands. Note, that rep might function a little weird in this case.
    *Fixed minor issue if players inventory doesn't consist of anything on first-join
    *Fixed minor issue with conditionals
    *Changed all 'Item' related to allow for no items (meaning you can now use the POI spawn without attachin items to core destruction, and open a blank itemwindow, accepting items)
    *Added new 'utility', 'thrashcan', allowing the player to easily delete multiple items by shift clicking into a blank window.
    *Added new requirement, 'PlayerName'. Can be used to black/whitelist specific players from/to a command.
    *Added new lang strings, Main_Error_BlacklistedName, Main_Error_NotWhitelistName, Main_Help_NameBlacklisted, Main_Help_NameNotWhitelisted
    *Added 'PlayerName' to help entries.
    *Fixed potential dupebug, for commands using cost-references, where one item could substitute another.
    *Added new lang strings, Main_Help_TooLittleCustom, Main_Help_NotExactCustom, Main_Help_TooMuchCustom.
    *Added valuta to help entries.
    *Added new lang string, Main_Error_NotEnoughCustom
    *Added valuta to cost
    *Fixed issue where cost would occasionally be ignored (Allowed to use, despite not having)
    *Added valuta pay to help entries.
    *Added to the I/O format of inventory, decay and ammo, as optional params.
    *Updated relevant replace, to handle 3-5 (), to make use of above.
    *Updated return of items, to make use of above.
    *Updated stringdifferences for item, to make use of above.
    *Added 'ammo' and 'decay' to item, when used to send
    *Added new file 'valutahelper.yaml' - for additional valuta data
    *Added new config key, 'ValutaHelperFile' (Default 'valutahelper.yaml')
    *Added new replace, 'ValutaResolve' (see docu). Interacts with the 'valutahelper' file, to give extended info on a valuta.
    *Added support for using 'Intervals' in cost > ValutaReference. (such as 'friendly'. Please make sure that the entry exist, as it will otherwise compare with value '0'). Keep in mind, that this is not exactly recommended, as it converts it to the value. For instance 'honored' would in 'TestFaction' example convert to 50, and hence cost player *50* every time. Make sure you intend to use it as such, if you do.
    *Added Initial to 'valutahelper.yaml': this can be used to set a default (what the valuta should start out as). It will be determined, first time an user needs the valuta.
    It is based on : 'Origin' (Their origin, int) > 'OriginPlayfield' (Their start playfield) > default (any with '-1' as value). If none were found, it will start as '0'.
    *Added possibility to have {} in valutareferences aswell (will be resolved).
    *Added support for using 'Intervals' in requirements > ValutaReference.
    *Added support for using 'Intervals' in help commands.
    *Added lang string Main_Error_ValutaWrongFormat, Main_Error_MissingArgument, Main_Success_AddedValuta, Main_Success_SetValuta
    *Added new command to set valuta. Syntax: '.cc vl <steamid> <valutaname> <value>' : Valutaname will be case insensitive, if an entry exist in 'valutahelper' (ie. if input is 'testfaction', and 'valutahelper' have an entry on 'TestFaction', it will autocorrect to this. Otherwise it will use the input). If value have a '+' infront, it will add to what the user have. Otherwise it will set it to this value. Note that the command, if 'add', will respect any default for the valuta, and add ontop of this.
    *Added new replace, ValutaList. See docu.
    *Added new replace, Function. See docu.
    *Added new 'Functions' Will allow for better and more dynamic use of previous for-loops (These still exist)
    *Now allowing 4 additional layers of command-nesting for for-loops (if you are using them, it is expected you know what you are doing, so the added complexity is to allow for more advanced use)
    *Fixed issue when using multiline combined with colons, in ChatMessages (Text, TextAll (..))
    *Added new replace, 'Length', giving the amount of chars a string contains (hello = 5, etc)
    *Added new replace, 'CharAt', giving the char at some position (0-indexed).
    *Added new 'default', 'valutaoverview.yaml', containing 'ReputationView' - Showing all valuta in 'typeof' reputation. Also containing 'CurrencyView' - Using the same approach, but intended for currencies.
    *Added tag 'Creator'. Can be used to 'sign your work', under an entry. It doesn't actually do anything, but is, obviously, visible in the file.
    *Added tag 'DefaultLanguage' to config, default 'English'.
    *Updated internal values to SpotGuard v1.33
    *Added listener for 'lang' in chatmessages, will update users lang. It will read the update after 10s.
    *If user exist on SpotGuards specialslist, and have a 'language' entry, it will use this as the local language. (Try to localize based on this).
    *When running a command, will always check for changed language by the user, based on SGs Specialslist.
    *Added debug timing to for-loops to give a better idea of how expensive they are (And to better help optimise them)
    *Tweaked behaviour of {break} in for-loops, to only break this iteration, rather than the entire thing. Use {return} to stop it entirely (and return the value), as would be expected behaviour of normal programming.
    *Added Que under lastused. Will make use of a dummy entry, 'System_Pending_Commands', one for each steamid. If they have anything qued, will be under 'Qued'
    *Added new replace, QueCommand, to que/execute a command for a steamid (if they are not online, command will be qued for next they are online)
    *Added ability to do {variable+=value}, to add it to the current. Note! this is a string addition, NOT numerical. (Meaning if you do +=2, and had 2 already, it would become 22 not 4).
    *Optimized for-loops, and especially functions a lot.
    *Added prefix for localized variables inside functions/for-loops: If using 'local_', they will only exist inside this scope (and be more efficient)
    *Now allowing 'defaultvalue's to be blank (""). Note that their default is still "NA" (ie. won't resolve to anything, unless specified)
    *Fixed issue with using the tag 'Special' under 'Item' under 'Action'. If passed "" (blank), it wouldn't deliver a blank stack, but rather return an error.
    *Updated helper function for inspectplayer, to no longer edit the 'executing players' inventory.
    *Added 'Extension' By Taelyn, VirtualBackpack. Script allowing to access two virtual backpacks for each player, by default.
    *Added translation for 'Dutch', thanks to Taelyn!
    *Added translation for 'Danish', thanks to Androggles!

    v1.01 (Important!)
    *Fixed issue where, when using a target, it would randomly overwrite as the executing player aswell.
    *Fixed API to actually be port-forwardable, rather than only locally. (Also both IP+localhost+127.0.0.1 should work locally)
    *Playerlocation now shipped with default utility - Allows for lookup on a player, and an admin tp to a player.

    Known Issues
    - You are currently not able to spawn any NPCs (Not yet implemented in the API)
    - You cannot use the Zoning hooks (Currently an API bug prevents this)
    - Only 'window' and 'POI' works for ItemDeliveryMethod (No way to do others yet)
    - Structures teleported to 0,0,0 or rotated to 0,0,0 outside this mods doing, will not have their position and rotation correctly given through {entityinfo}, until next server restart (Game bug)

    If you find any issues, please let me know.
    If you have an idea for something, and cannot find a way to do it, I will look into how difficult/if at all possible, to implement a way to allow it.
     
    #2
    Last edited: Jun 2, 2019
    Ephoie likes this.
  3. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    Command Packs
    By default, two folders of commands are shipped with the mod. These are meant as inspiration, rather than actual commands for the most part, and will likely need to be at the very least tuned, to fit what you want.
    Feel free to uncomment them alltogether, and just create your own tho!

    Download: http://welf.dk/Empyrion/CC/default - Please always overwrite/
    License: Use, Edit, Republish as you want.

    -DeathMessages
    This is a demo command, that hooks onto a player death, showcasing that you can build an entire deathmessenger if you want. It only features 'type 1' death, so some work is needed on your part, if you want to use it as an actual thing.

    -Demostuff
    --TestTakeMyCSV
    This command spawns a POI 100 meters from the player. Upon destroying the core, it will reward the player.
    It is recommended to uncomment it, and only take it as an example of what you can do.
    --demoreload
    This command is a demo of using the 'external calls' - ie, making a GET/POST to another server. The demo interacts with the API of the mod, if enabled.
    --demo_Working_with_Requests
    This command is a demo of using 'additional data'. In some cases, you might want to get all the data for a call, rather than just what the mod is serving you. This is a demo for getting all entities for a playfield, and narrowing them down to one entity.
    If you need further 'direct data' requests, please do ask.

    -Messaging
    This command features a simple whisper and reply command, that players can use to talk with eachother silently.
    This must be used with the 's! ' prefix, to actually be silent tho, or at the very least, used in their own factionchat.
    Also features a /dnd mode, where a player can tag themself as not wanting messages.

    -SillyAdditions
    As the name implies, additions that really doens't serve much of a practical purpose, but rather is there for the fun of it.
    --TakeABreak
    A command showcasing starting a command when a player signs on, and then doing several things delayed. Will give them a message every 2 hrs, telling them to take a break ;)
    --Kill
    A command that is practically /suicide, for a normal player, but can be used to kill a specific player as administrator.
    --cakeisnotalie
    Well! It's not! A command to give all players a cake.
    --playerfirstjoinannouncement
    A command that utilises limitmaxuses, to only run once per player.
    A welcome message together with giving every online player a cake, telling them about the new player.
    --Roll
    A simple command, that utilises randomness, to generate a number between two intervals, that is definable. By default 0-100, but can be x to y.

    -valutaoverview
    --ReputationView
    Demo usuage of making a visual representation of the users 'reputation' (from valuta)
    Download: http://welf.dk/Empyrion/CC/utility - Please always overwrite/
    License: Use, Edit, Republish as you want.

    -Adminutility
    --InspectPlayer
    Allows you to see what a player have in their inventory. If you make changes to it, it will update their inventory with the changes.

    -PlayerItemTransfer
    --TransferItem
    Allows a player to transfer any amount of any item (that they have), to another player, on the same playfield.

    -Teleport
    --TPAB
    Allows an admin to teleport one player to another
    --Teleport
    Allows a player to teleport to another, if the other player accepts it.
    --Back
    Allows a player to go back to where they were, before they were teleported.
    (Can easily be used as a template for "sethome" and "gohome", if you so desire)

    -VoteKick
    Allows a player to initate a votekick against another, if no admins are around.
    if 2/3 of players online agrees with the kick, the other player will be tempbanned for an hour, and kicked.

    -PlayerLocation
    --lookup/find
    Allows you to lookup a player, and get information on them, such as faction, playfield, xyz
    --AdminTeleport
    Allows any admin to teleport to anyone. (Not having to worry about coords/playfield/etc, just type name/clientid/entityid/steamid of target)
    Note, supports /back, to return to where teleproted from.

    -thrashcan
    Simply allows the player to easily discard things they don't want.

    -scatterplayers
    Allows to scatter players in a radius, around a point, on a playfield. Experimental. Please provide feedback.
    Download: http://welf.dk/Empyrion/CC/Extensions/
    License: Use, Edit, Republish as you want.

    -virtualbackpack
    --vb
    Author: Taelyn
    Allows the user two virtual backpacks, where stuff can be stored and retrieved at will.

    If you are using Discord, you may be interested in checking out @Taelyn 's bot: https://empyriononline.com/threads/discord-egs-bot.42672/
    It, among other things, allows you to list players online, directly into the discord, aswell as reload CCs files remotely.

    Contributing
    If you want to share your creations, please do!
    If you want them to be featured, consider sending them directly to me.
    Please also specify, if you are alright with having them included in the default download or not. If yours is 'commonly usuable/desirable', I'd like to include it to the default :)

    If you are new to YAML editing overall, I'd highly recommend the software "Sublime Text Editor". It does a good job at making YAML easy to read, and easier to spot when you make mistakes. Keep in mind, that YAML is a *very* case-sensitive language.
     
    #3
    Last edited: Aug 30, 2018
    Ephoie likes this.
  4. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    This mod introduces four files:
    'lastused.yaml'
    and
    'permadata.yaml'
    and
    'valuta.yaml'
    and
    'valutahelper.yaml'
    The lastused file contains a history of when every command is executed by every player (only one per player/entry. Newest data for that is saved). Aswell as how many times it have been used in total by that player (including from nesting), and how many times the user can use it (if 1.3 is used). You can edit this file while the server is running.
    The permadata file stores permanent variables, set from 1.13.3. If changes are made to this file, while running, they will be ignored. While the server is running, this file is effectively read-only (as any time an entry saves, it will overwrite the file).
    The valuta file stores the players standing/valuta. This can be used to make custom currency, or as reputation like.
    The valutahelper can be used to store additional data on valuta, such as a description, initial value, and intervals.
    Additionally, it have a 'config.yaml' file.
    For documentation on this file, see below. Everything the mod does, is based on the config file!

    This can be a rather complex mod. For the most part, you can accomplish pretty much anything you can think of however. And you only have to use, what is relevant to accomplish this.

    First, I will go over each tag, one by one. It will be ordered by x.y.z, as to 'where we are in the yaml' (how many indentations / what is above)
    You can refer to the file 'configentrytemplate.yaml', for the whole thing. This explanation accompanies that list.
    Code:
    
    1 - Entries:
     Each of these entries are a command or function that are tied to an 'EntryKey' (see 1.1), and the parameters that follow.
     
     1.1 - EntryKey:  
      (String, REQUIRED)
      This is the name of command (and what is shown to players)
      Each entry has a key. A key is a phrase entered by the player or mod to initiate a command. The key should be unique (if used for nesting). The key can be any word or string of letters.
      DO NOT use variables for this.
      DO NOT use spaces, or special characters from this.
      (It will likely work, but will also quite likely lead to really weird behaviour at times)
      DO NOT use localization for this (It might work out fine, but you might find it behaving weird, and it will allow players to exploit this command, by changing their language).
    
     1.2 - LimitMaximumUses:  
      (Int, optional, default: '0' - no limit)
      This parameter determines how many times may each user use this entry. If the limit is reached, the user can no longer use it. You can manually reset or change how many times the user have used it, by going into the 'lastused' file, finding the 'EntryKey', (1.1), and removing the entry or just changing the entry 'AmountUsed'
     
     1.3 - RequireCharges:  
      (Boolean, optional, default: 'false' - No charges required)
      If set true, the user would require charges in order to use this command. Charges values are set manually via the 'lastused' file.  You can manually give the user charges by going into the 'lastused' file, find and edit the entry/adding the entry, by it's EntryKey' (see 1.1) and editing 'ChargesLeft'. (10 = 10 charges left. 1 charge is used per time the entry is fired)
     
     1.4 - CommandEntry:  
      (List, required if you want this command executed.)
      This is the 'base' for the entry. What's written here triggers the function and is required to invoke it.
      Please note the positions below, referring to the seperation of the command functions. 
      For instance - if a user (typically admin) types the following... 
       !kill 'john doe' silently 
      we have three positions to the command:
       Position 0 - kill (this is the command, in this case 'kill' which requires a target)
       Position 1 - john doe (target) incased in "" or '' when there are spaces in the name
       Position 2 - silently [additional parameter of the command function)
      
      1.4.1 - i:   
       (Int, Required)
       This is the position of entry values in the input that the mod looks for when triggered. (see 1.4)
      
      1.4.2 - key:   
       (String, optional)
       if specified, this will later be used to refer to as a {key} 
       Example - if you use "key: cheese", it will later be referenced by {cheese}, in this entry
       Please note, that if the 'type:' (1.4.3) for this 1.4 is 'item', the {key} will be the ID of the item, if found, and the {key_name} will be the actual name of the found item.
       
      1.4.3 - type:   
       (String, optional, default: 'key')
       What is expected here? (Will validate the input, and only trigger, if input fits.. For instance, if type is 'playername', and it did not match the name of an online player, it will not trigger)
       - 'string': any phrase or string of letters/numbers will work here
       - 'int': Needs to be a number value
       - 'boolean': Needs to be set as 'true' or 'false'
       - 'key': Expected is what was written in 1.4.2 (use this to make normal constructions.. for instance, if you want to make a command that trigger on !kill, you would write in 1.4.2: 'kill', and in 1.4.3 'key')
       'entityid': Expected is the id of an entity (although it will only accept players atm)
       - 'playerid': Expected is the 'entityid' of an online player
       - 'playername': Expected is the name of an online player
       - 'playername+': Expected is some input, that will resolve to an online player (for instance, if a player named 'Exacute' is online, 'Exa' will do, or 'E' (..)). If what is entered matches several, the last-found will be what is used.
       - 'client': Expected is the clientid of an online player
       - 'steam': Expected is an actual steamid, for someone online
       - 'poi': Expected is the name of a POI on any playfield
       - 'poispecial': Expected is a vessel
       - 'poiall': Expected is anything that goes for a POI (including asteroids, ressources, etc)
       - 'playfield': Expected is the name of a playfield.
       - 'faction': Expected is either the full name, or the abbrevation of a faction.
       - 'item': Expected is either the name of an item, or its ID. (Resolved against the config_example.ecf file. If not found there, will attempt to be resolved against localization)
      
      1.4.4 - optional:   
       (Boolean, optional, default: 'false')
       Determines if this is optional to be input, in order to make this entry run (if true, it will not require it, but will take it, if specified)
      
      1.4.5 - accept:   
       (String, optional, default: 'any')
       Please note that these only work for 'type' 'entityid' or 'playerid' or 'playername' or 'playername+' or 'steam' or 'client'. Whatever is written here will be ignored otherwise.
       Acceptable parameters....
       - 'samefaction': Will require the specified entry to be of the same faction as the player executing this entry.
       - 'sameorigin': Will require the specified entry to be of the same origin as the player executing this entry.
       - 'onlyfriendly': Will require the specified entry to be friendly to the player executing this entry. (Note, that currently this means 'in the same factiongroup')
       - 'sameplayfield': Will require the specified entry to be on the same playfield as the player executing this entry.
       - 'otherplayfield': Will require the specified entry to be on the another playfield than the player executing this entry.
       - 'any': Will accept anything (default)
      
      1.4.6 - useastarget:   
       (Boolean, optional, default: Depends on the input type (see below))
       Determines wether what is typed here, will be attempted to be used as the 'target player'.
       Will default to true, if type is 'entityid' or 'playerid' or 'playername' or 'playername+' or 'steam' or 'client' or 'faction'
       Will otherwise default to false.  
       If it's used on a type 'poi', it will take all players near a poi of that name
       If it's used on a type 'playfield', it will take all players on that playfield
       If it's used on a type 'faction',  it will use all members of that faction
      
      1.4.7 - whitelist:   
       (String, optional, default: 'NA' - everything is allowed, aslong as it fits the 'type' (see 1.4.3)'
       If specified, will require the entry to be on this list.
       You can type a list, seperated by commas, of accepted entries. For instance 'TradingStationAkua,TradingStationOmicron'. It would only accept the input, if the input is either 'TradingStationAkua' or 'TradingStationOmicron'
       Note that the entry must still match what is specified in the type (see 1.4.3) in addition to be on this list.
       If you wish to do fuzzy searching, you can do * in front, in end, or both sides.
       If no stars, must match EXACT
       If star in front, can match exact, or have something in front
       If star in back, can have something in back, but not in front
       if star on both sides, can be anywhere in the string
    
      1.4.8 - blacklist:   
       (String, optional, default: 'NA' - everything is allowed, aslong as it fits the 'type' (see 1.4.3)'
       If specified, will require the entry to NOT be on this list.
       You can type a list, seperated by commas, of accepted entries. For instance 'TradingStationAkua,TradingStationOmicron'. It would only accept the input, if the input is either 'TradingStationAkua' or 'TradingStationOmicron'
       Note that the entry must still match what is specified in the type (see 1.4.3) in addition to be on this list.
       If you wish to do fuzzy searching, you can do * in front, in end, or both sides.
       If no stars, must match EXACT
       If star in front, can match exact, or have something in front
       If star in back, can have something in back, but not in front
       if star on both sides, can be anywhere in the string
      
      1.4.9 - defaultvalue:   
       (String, optional, default: 'NA' - no default. If not specified, will resolve to nothing)
       Only does anything, if the key (see 1.4.2) is used.
       If anything is specified, it will use this as the default value, for that key.
       (For instance, if you have a key called 'amount', that is optional, and nothing is written. Or the key isn't triggered, because another condition was true before it, the {amount} will resolve to this value).
       Can be used with the type 'string','int','boolean','item' (see 1.4.3).
    
      1.4.10 - min:
       (int, optional)
       If you are using 'int' for 1.4.3, you can specify this as the minimum (=>) the number must be, to be valid.
       
      1.4.10 - max:
       (int, optional)
       If you are using 'int' for 1.4.3, you can specify this as the maximum (=<) the number must be, to be valid.
        
     1.5 - BackupTarget:   
      (String, optional, default: 'self')
      If no targets were found from the 1.4, this target will be used.
      Can be.... 
      - 'self': Will default to the executing player.
      - 'faction_<name>': Will default to the faction of this name. (faction_test (=> use faction 'test' as target (every player in that faction)))
      - 'all': Will default to using every online player as target
      - 'playfield_<name>': Will default to use every player on a playfield as target (playfield_test (=> use playfield 'test' as target (every player on that playfield)))
      - 'poi_<playfield>_<name>': Will default to use every player near a POI with a given name, on a playfield, as target (poi_pahik_test => use playfield 'pahik's POI(s) 'test' as target (every player near that poi))
     
     1.6 - OverrideTarget:   
      (String, optional, default: 'NA' - non-overriding)
      This entry will override the CommandEntry (see 1.4) and BackupTarget(see 1.5), with this target.
      Can be
      - 'self': Will use the executing player.
      - 'faction_<name>': Will use the faction of this name. (faction_test (=> use faction 'test' as target (every player in that faction)))
      - 'all': Will default to using every online player as target
      - 'playfield_<name>': Will use every player on a playfield as target (playfield_test (=> use playfield 'test' as target (every player on that playfield)))
      - 'poi_<playfield>_<name>': Will use every player near a POI with a given name, on a playfield, as target (poi_pahik_test => use playfield 'pahik's POI(s) 'test' as target (every player near that poi))
     
     1.7 - TargetAccept:   
      (String, optional, Default: 'NA' - not used, no accept required)
      If specified, will send this string to the targeted player, and will require, that that player types .accept within 5 minutes, in order to execute the entry. If the target doesn't respond, or types .deny, it will not be executed (and the executing player will not be charged the cost).
      What is specified here will be sent to the player. For instance 'TargetAccept: "{PlayerName} wishes to teleport to you."'. It will automatically append 
       '. To accept type /accept, deny: /deny. Expires in 5m.', 
      to whatever you have specified.
     
     1.8 - Requirements:  
      (List, optional. Remove the tag if not used.)
      For each entry, it will require everything of that entry to be true.
      If the entries are set 'true', it will activate the function.
      Please note that each of the requirements entries are an 'OR' function. If one succeeds => then an entry is allowed to proceed.
      In order the requirements entry to succeed, all specified entries must be set 'true'.
      Also note that due to the nature of AND functions, using both 'Below' and 'Exact', or 'Above' and 'Below', or any other combination of the same functions below (reputation, vip, admin, playtime), it will FAIL and not proceed.
      
      1.8.1 - BelowRep:   
       (Double, Optional)
       Requires the executing player to have reputation less than this.
      
      1.8.2 - ExactRep:   
       (Double, Optional)
       Requires the executing player to have reputation exactly this.
      
      1.8.3 - AboveRep:   
       (Double, Optional)
       Requires the executing player to have reputation more than this.
      
      1.8.4 - BelowVIP:   
       (int, Optional)
       Requires the executing player to have VIP rank less than this.
      
      1.8.5 - ExactVIP:   
       (int, Optional)
       Requires the executing player to have VIP rank exactly this.
      
      1.8.6 - AboveVIP:   
       (int, Optional)
       Requires the executing player to have VIP rank more than this.
      
      1.8.7 - BelowAdmin:   
       (Double, Optional)
       Requires the executing player to have Admin permission less than this.
      
      1.8.8 - ExactAdmin:   
       (Double, Optional)
       Requires the executing player to have Admin permission exactly this.
      
      1.8.9 - AboveAdmin:   
       (Double, Optional)
       Requires the executing player to have Admin permission more than this.
      
      1.8.10 - BelowPlaytime:   
       (Double, Optional)
       Requires the executing player to have played (in minutes) less than this.
      
      1.8.11 - ExactPlaytime:   
       (Double, Optional)
       Requires the executing player to have played (in minutes) exactly this.
      
      1.8.12 - AbovePlaytime:   
       (Double, Optional)
       Requires the executing player to have played (in minutes) more than this.
    
      1.8.13 - ValutaReferences:
       (List, Optional)
       If you want to reference a valuta for Requirement, use this.
    
       1.8.13.1 - 'Entry':
       (String, Int, required if using 1.8.13)
       Specify what you want to compare.
       For instance, if you have a valuta entry, 'TestFaction', you can do
       'Above'
       'Exact'
       'Below'
       Followed by 'TestFaction' (ie. 'AboveTestFaction', if you want to check if the valuta entry contains something greater than this)
       For the value, specify an integer to test it against.
       (For instance, 'AboveTestFaction: 22' will test if user have more than 22 'TestFaction' in valuta) 
    
      1.8.14 - PlayerName:
       (String, optional)
       You can limit it to only being players on a list, able to use this command. CSV goes.
       If you start the entry with a '!', it will be a blacklist, rather than whitelist:
       For example: 'Exacute,Taelyn' : Only these two players are allowed to use the command
       Other example: '!Taelyn,Rexxxus' : Everyone but Taelyn and Rexxxus is allowed to use the command.
     
     1.9 - Cost:   
      (List, optional (remove tag if you are not taking anything from the player))
      In order to use this entry, the executing player must meet these requirements (be able to pay).
      
      1.9.1 - Credits:   
       (int, optional, default: '0')
       The user must have above this amount of credits. This amount of credits will be taken, if the entry is executed.
      
      1.9.2 - Reputation:   
       (Double, optional, default: '0')
       The user must have above this amount of Reputation. This amount of Reputation will be taken, if the entry is executed.
      
      1.9.3 - MaximumCostOfItems:   
       (int, optional, default: '0' - No upper limit => the limit is the amount of entries in 'items' (see 1.9.6))
       If you specify a number here, it will limit the amount of picks from the 'items' list (see 1.9.6) to this number. (for instance, if 9 items, and you put this to 3, only 3 will be picked). 
    
      1.9.4 - MinimumCostOfItems:   
       (int, optional, default: '0')
       Similar to 'MaximumCostOfItems' (see 1.9.3), but the minimum amount that will be picked. Must be LESS THAN 'MaximumCostOfItems', or 'MaximumCostOfItems' must be 0
      
      1.9.5 - AllowDuplicates:   
       (Boolean, optional, default: 'false' - don't allow duplicates)
       This will determine the in order which to get the desired amount of picks from 'MinimumCostOfItems' (see 1.9.4) to 'MaximumCostOfItems' (see 1.9.3), an entry on 'Items' (see 1.9.6) can be used several times. 'True' => yes, can be reused. 'False' => no (false means there must be atleast 1.9.3 amount of items on the list)
      
      1.9.6 - Items:   
       (List, optional - remove tag if you are not taking any items from the player)
       Note, that the player must have enough of ALL the items specified, in order to proceed, no matter what will actually be 'drawn' for payment. (This is so players cannot abuse the system, to only have some of the cheaper items, and that way force the system to only take the lowest cost)
       
       1.9.6.1 - Item:   
        (String, required if using 'Items' (see 1.9.6))
        Which item should be taken from the player?
        You must specify the KEY of the item (from config_example.ecf. For instance 'iron ingot' would be 'ironingot').
        You may use a shorter version, if you want to allow for any item matching, to 'be used for this slot'. (For instance 'iron' would allow for both 'ironingot' and 'ironore')
        Note, that you in addition to using ambigue text ('iron' for instance), can use a CSV, to allow several entries.
        You could for instance specify 'Shotgun Shell,Iron', if you want to allow for 'ironingot, ironore, shotgunshell'. Note however, that the player must have atleast the amount (1.9.6.2) of ONE of these items. (If amount is 2, and player only one shotgunshell, and one ironore, they will be denied. If player have one shotgunshell and two ironore, the two ironore will be taken. If player have two shotgunshells and two ironore, two shotgunshells will be taken. The order in the list means which to pick first)
       
       1.9.6.2 - Amount:   
        (Int, optional, default: '1' - 1 x the item)
        Amount of the Item specified in 'Item' (see 1.9.6.1)
       
       1.9.6.3 - ContainPercent:   
        (Int, optional, default: '100' (High chance))
        This is effectively the weight of this 'item entry'. The larger the weight, the more chance that it will be picked for the cost in the end.  For convenience, this should be set on a scale from 0-100 (0 being practically never).
    
      1.9.7 - ValutaReferences:
       (List, Optional)
       If you want to reference to valuta, as a cost, use this.
    
       1.9.7.1 - 'Entry':
        (String, Int, required if using 1.9.7)
        Specify what you want to cost.
        For instance, if you have a valuta entry, 'TestFaction', you can do
        TestFaction: 10, to let it cost 10
        For the value, specify an integer to take.
        (For instance, 'TestFaction: 22' will take 22 'TestFaction' valuta from the user) 
      
      1.10 - FactionShareCooldown:   
       (Boolean, optional, default: 'false' - Doesn't share cooldown time)
       Should the entire faction of the executing player share the cooldown on this? (If one is on cooldown, noone else in the faction will be able to use the command, untill the player trying, meets the cooldown argument(s))
       True = share, false = doesn't
      
      1.11 - Reset:   
       (List, Optional - Remove the tag if no cooldown).
       This is logic. If ANY 'ResetCondition' (see 1.11.1) is set true, it will trigger reset. (OR)  In order for a ResetCondition to be true, all parts of it must be true (AND)
       
       1.11.1 - ResetCondition:   
        (List, required if using 'Reset' (see 1.11))
        All entries have to be true, in order for this to be true.
        If this is true, the reset will trigger.
        
        1.11.1.1 Requirements   
         (List, Optional - remove the tag if no 'requirements' (see 1.8))
         Functions the same as 1.8 . Read that chapter for documentation.
         If specified, requires the user to fullfill one of the conditions of the Requirements, in order to be allowed to use this ResetCondition. (Fails the ResetCondition, if user doesn't fullfill any requirements)
        
        1.11.1.2 Cooldown:   
         (Int, optional)
         If specified, will require this amount of time in minutes to have passed, since the last execution of this entry. (user can be on, or offline)
        
        1.11.1.3 CooldownIngame:   
         (Int, optional)
         If specified, will require this amount of time in minutes to have passed, with the user being on the server (playing), since the last execution of this entry. (User have to be online for this amount of minutes)
        
        1.11.1.4 - ResetDay:   
         (Int, optional)
         If specified, will require it to have been atleast 'the next' of this day, in the month, in order to reset. (if last execution is the 30th, and ResetDay is 2, it will reset the 2nd of the coming month. If last execution is the 30th, and ResetDay is 31, it will reset the next day). Note that funkyness might be related, if you use >28. (might require the user to wait several months). It is recommended to use 1-28 for that reason.
        
        1.11.1.5 - ResetNear:   
         (String, optional)
         If specified will require it to have been 'the next' of this weekday.  Entry can be 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'.
         Example - If last execution was on a tuesday, and reset is thursday, user must wait two days. If last execution was on a tuesday, and reset is monday, user must wait six days. If it's the same day, as the resetday, the user will have to wait seven days.
      
      1.12 - TriggerReset:   
       (List, optional - remove if you don't want to share the cooldown time)
       If specified, it will behave as if 'these other entries' were also executed, in relation to the cooldown time. (1.11 can be individual for them, but the 'last use', is 'now')
       
       1.12.1 - ResetEntryKey:   
        (String, required if you use 'TriggerReset' (see 1.12))
        Must be a reference to an 'entrykey' (see 1.1)
      
      1.13 - Actions:   
       (List, optional - if not specified, nothing happens. This is required to be set if desired to function)
       This is what happens, when the above conditions are successfully met (The user have the materials (cost, 1.9), the user meets the requirements for using the entry (Requirements, 1.8), and the user have met a reset condition (Reset, 1.11)
       
       1.13.1 - Conditionals:   
        (List, optional - Remove the tag if no additional 'conditions' (see 1.13.1.1) are set)
        You can use 'Conditionals', if you wish to require the player to meet additional requirements, for executing this Action-entry. If the user doesn't meet them, this one will simply be skipped.
        The user must meet atleast ONE 1.13.1.1, Conditions, for this action-entry to executed. (OR)
        
        1.13.1.1 - Conditions:    
         (List, required if you use 'Conditionals' (see 1.13.1))
         In order for 'Conditions' to be true, all the 'Condition' entries (see 1.13.1.1.1) must be set 'true' (AND)
         
         1.13.1.1.1 - Condition:   
          (String, required if you use 'Conditions' (see 1.13.1.1))
          In order for the 'Conditions' (see 1.13.1.1)  to be set 'true', this must also be set 'true'.
          You can use logic, and variables here. Refer to the spoiler of "String-replacement", and "Using Conditions (logic)" for further documentation on this.
       
       1.13.2 - ExecuteDelay:   
        (float, optional, default: '0' - no delay)
        If specified, the mod will wait the specified amount of time in SECONDS to execute this entry. You may use decimals, such as '0.5', to make half a second.
        
       1.13.3 - StoreData:   
        (List, optional - remove this tag if you don't want to store any data)
        If you want to store data permanently in the 'permadata' file, and create entries of this information. The data can be accessed by using the 'RefKey' in {RefKey}.
        Please note that if data for the RefKey exist already, it will be overwritten.
        
        1.13.3.1 - RefKey:   
         (String, Required)
         What this can later be referred to with. Examples can be in writing. See below examples....
         'didfirst': Can be accessed with {didfirst}
         'originalPlayfield-{PlayerID}': You can use variables with it. This can later be accessed with {originalPlayfield-{PlayerID}}, where {PlayerID} resolves to the id of the currently executing player. Refer to the spoiler 'String replacement', for further documentation.
         
        1.13.3.2 - Data:   
         (String, Required)
         What data do you want to be stored for this RefKey. You can use varibles (for example '{PlayerPlayfield}' would store the current playfield's name, where the executing player is at). Please refer to the spoiler 'String replacement', for further documentation.
    
        1.13.3.3 - File:
         (String, optional, default: 'permadata')
         If specified to anything BUT 'permadata', will create/save to this file, in /permadata folder. If file doesn't exist, it will be created.
         If specfied to 'permadata', it will save as default 'permadata.yaml', or how it is configured.
         Note, that you ONLY specify a name. ie, if you want it to be saved to a file named 'bounty.yaml' in 'permadata' folder, you simply specify 'bounty'.
         
       1.13.4 - ExecuteMaximumAmountOfCommands:   
        (Int, optional, default: '0' - will use the amount of executecommands)
        Similar to the 'pick items for cost', you can specify a min-max for commands to be executed.
        This is the maximum. Must be greater than 'ExecuteMinimumAmountOfCommands' (see 1.13.5) if specified
        
       1.13.5 - ExecuteMinimumAmountOfCommands:   
        (Int, optional, default: '0' - no minimum)
        This is the minimum amount of commands to execute.
       
       1.13.6 - ExecuteCommandsAllowDuplicates:   
        (Boolean, optional, default: 'false' - No duplicates)
        In order to meet the min/max of 'ExecuteMaximumAmountOfCommands' (see 1.13.4),and 'ExecuteMinimumAmountOfCommands' (see 1.13.5) This asks if one command be executed more than once.
        If true = yes, false = no
        
       1.13.7 - ExecuteCommand:   
        (List, optional (Remove if you are not using it))
        If you, in addition to 1.13.16, or exclusively, wants to execute happenings, you can use this.
        What is executed, depends on 1.13.4, 1.13.5
        
        1.13.7.1 - UniqKey:   
         (String, REQUIRED)
         Must be UNIQUE for this ExecuteCommand. Otherwise weirdness is assured. Can be anything, aslong as it is unique, relative to 'ExecuteCommand' (see 1.13.7)
    
        1.13.7.2 - Text:   
         (String, optional)
         If specified, will send this text to the executing player in chat
        
        1.13.7.3 - TextTarget:   
         (String, optional)
         If specified, will send this text to the target player in chat
        
        1.13.7.4 - TextAll:   
         (String, optional)
         If specified, will send this text to all players in chat
        
        1.13.7.5 - TextPlayfield:   
         (String, optional)
         If specified, will send this text to all players on the same playfield, as the executing player in chat
        
        1.13.7.6 - BoxRed:   
         (String, optional)
         If specified, will send this text to the executing player in a red box
        
        1.13.7.7 - BoxRedTarget:   
         (String, optional)
         If specified, will send this text to the target player in a red box
        
        1.13.7.8 - BoxRedAll:   
         (String, optional)
         If specified, will send this text to all players in a red box
        
        1.13.7.9 - BoxRedPlayfield:    
         (String, optional)
         If specified, will send this text to all players on the same playfield, as the executing player in a red box
        
        1.13.7.10 - BoxYellow:   
         (String, optional)
         If specified, will send this text to the executing player in a yellow box
        
        1.13.7.11 - BoxYellowTarget:   
         (String, optional)
         If specified, will send this text to the target player in a yellow box
        
        1.13.7.12 - BoxYellowAll:   
         (String, optional)
         If specified, will send this text to all players in a yellow box
        
        1.13.7.13 - BoxYellowPlayfield:   
         (String, optional)
         If specified, will send this text to all players on the same playfield, as the executing player in a yellow box
        
        1.13.7.14 - BoxBlue:   
         (String, optional)
         If specified, will send this text to the executing player in a blue box
        
        1.13.7.15 - BoxBlueTarget:   
         (String, optional)
         If specified, will send this text to the target player in a blue box
        
        1.13.7.16 - BoxBlueAll:   
         (String, optional)
         If specified, will send this text to all players in a blue box
        
        1.13.7.17 - BoxBluePlayfield:   
         (String, optional)
         If specified, will send this text to all players on the same playfield, as the executing player in a blue box
    
        1.13.7.18 - ConfirmBox:   
         (String, optional)
         If specified, will send this text to the executing player in a confirm box (box with OK button)
         For confirmbox, remember \r and \n (either works), will make newline. NOTE THAT THE TEXT MUST BE WRAPPED IN "", in order for \r and/or \n to do anything (otherwise it's just read as text)
    
        1.13.7.19 - ConfirmBoxTarget:   
         (String, optional)
         If specified, will send this text to the target player in a confirm box (box with OK button)
         For confirmbox, remember \r and \n (either works), will make newline. NOTE THAT THE TEXT MUST BE WRAPPED IN "", in order for \r and/or \n to do anything (otherwise it's just read as text)
        
        1.13.7.20 - ConfirmBoxPlayfield:   
         (String, optional)
         If specified, will send this text to all players on the same playfield, as the executing player in a confirm box (box with OK button)
         For confirmbox, remember \r and \n (either works), will make newline. NOTE THAT THE TEXT MUST BE WRAPPED IN "", in order for \r and/or \n to do anything (otherwise it's just read as text)
    
        1.13.7.21 - ConfirmBoxAll:   
         (String, optional)
         If specified, will send this text to all players in a confirm box (box with OK button)
         For confirmbox, remember \r and \n (either works), will make newline. NOTE THAT THE TEXT MUST BE WRAPPED IN "", in order for \r and/or \n to do anything (otherwise it's just read as text)
    
        1.13.7.22 - ConfirmBoxButton:   
         (String, optional, default: 'Close')
         Does nothing on its own. If you are using 1.13.7.18, 1.13.7.19, 1.13.7.20 or 1.13.7.21, this will change what the 'ok button' says.
        
        1.13.7.23 - ConsoleCommandSelf:   
         (String, optional)
         If specified, will execute this CONSOLE COMMAND on the executing player. Remember to encase variables, that may resolve to something with spaces (for instance {PlayerName} or {PlayerPlayfield}, etc), in '{PlayerName}'. Otherwise the command will break. (not be executed)
         You may want to refer to https://empyrion.gamepedia.com/Console_Commands for help on commands (Might not always be up to date. Help maintain it, if you can!)
        
        1.13.7.24 - ConsoleCommandTarget:   
         (String, optional)
         If specified, will execute this CONSOLE COMMAND on the targeted player. Remember to encase variables, that may resolve to something with spaces (for instance {PlayerName} or {PlayerPlayfield}, etc), in '{PlayerName}'. Otherwise the command will break. (not be executed)
         You may want to refer to https://empyrion.gamepedia.com/Console_Commands for help on commands (Might not always be up to date. Help maintain it, if you can!)
        
        1.13.7.25 - TelnetCommandSelf:   
         (String, optional)
         If specified, will execute this TELNET COMMAND (For symmetri, there's both a self and target. Effectively the same, but this uses {Player*} references from 'self')
         You may want to refer to https://empyrion.gamepedia.com/Telnet_Commands for help on commands (Might not always be up to date. Help maintain it, if you can!)
        
        1.13.7.26 - TelnetCommandTarget:   
         (String, optional)
         If specified, will execute this TELNET COMMAND (For symmetri, there's both a self and target. Effectively the same, but this uses {Player*} references from 'target')
         You may want to refer to https://empyrion.gamepedia.com/Telnet_Commands for help on commands (Might not always be up to date. Help maintain it, if you can!)
        
        1.13.7.27 - BreakInterval:   
         (Boolean, optional, default: 'false')
         If set 'true', and there is currently a 'do every n-seconds' running of this entry, for this player, will stop it.
        
        1.13.7.28 - NestToEntry:   
         (List, optional - Remove tag if not in use)
         Do you want to nest to additional entries? (The reason it's located here, rather than on the 'ExecuteCommand' function (see 1.13.70, is so that you can use random parameters, if you so desire..)
         
         1.13.7.28.1 - PassCommand:   
          (String, required)
          What command should be attempted to be executed (like if it were typed by the player ingame). You can, and should, use references (See spoiler 'string replacement' for more on this)
         
         1.13.7.28.2 - TriggerNestCooldown:   
          (Boolean, optional - default: 'false')
          This is unstable. In some cases it will work, most it will not do anything.
          If true, will trigger the cooldown for this nested entry.
          If false, will 'execute it for free' cooldown wise.
         
         1.13.7.28.3 - BypassNestCooldown:   
          (Boolean, optional - default: 'false')
          This is unstable. In some cases it will work, most it will not do anything.
          If true, will execute no matter if the nested entry is on cooldown for the player, or not. If false, will respect the cooldown.
         
         1.13.7.28.4 - IgnoreRequirements:   
          (Boolean, optional - default: 'false')
          This is unstable. In some cases it will work, but in most, it will not do anything.
          If set 'true', will ignore the Requirements section for the nested entry.
          If set 'false', will respect it (not execute it, if user don't meet requirements)
        
        1.13.7.29 - ExternalCall:   
         (List, optional - Remove if unused)
         Make a GET (no post data), or POST call to an external source.
         
         1.13.7.29.1 - CCurl:   
          (String, required if using 'ExternalCall' (see 1.13.7.28))
          Define the url to call. You are allowed to use ?&etc
    
         1.13.7.29.2 - CCreturn:
          (String, optional)
          Define a command to call, once returned data is received.
          You may use {returneddata}, which will be replaced by everything that was received. Note, that you may want to encase this in ''
         
         1.13.7.29.3+ - <custom>:   
          (String, optional)
          You may use *ANY* 'key' for these, and *ANY* value.
          somerandomkey: somerandomvalue
          Cow: MeOw
          Anything goes. What is typed here is what is POSTED to the defined url.
        
        1.13.7.30 - ExecuteAsTarget:   
         (List, optional (remove if not used))
         You can use this to 'simulate' another user executing a command, by specifying it here.
    
         1.13.7.30.1 PassCommand:
         (String, required (if you use 1.13.7.30))
         Which command do you wish to emulate sending?
    
         1.13.7.30.2 - TriggerNestCooldown:   
          (Boolean, optional - default: 'false')
          This is unstable. In some cases it will work, most it will not do anything.
          If true, will trigger the cooldown for this nested entry.
          If false, will 'execute it for free' cooldown wise.
         
         1.13.7.30.3 - BypassNestCooldown:   
          (Boolean, optional - default: 'false')
          This is unstable. In some cases it will work, most it will not do anything.
          If true, will execute no matter if the nested entry is on cooldown for the player, or not. If false, will respect the cooldown.
         
         1.13.7.30.4 - IgnoreRequirements:   
          (Boolean, optional - default: 'false')
          This is unstable. In some cases it will work, but in most, it will not do anything.
          If set 'true', will ignore the Requirements section for the nested entry.
          If set 'false', will respect it (not execute it, if user don't meet requirements) 
    
        1.13.7.31 - ExecutePercent:   
         (Int, optional, default: '100' - high chance)
         Similar to 1.9.6.3, this is how large a chance 'this executecommand' is picked, for the min-max.
       
       1.13.8 ItemDeliveryMethod:   
        (String, optional, default: 'window')
        If items are part of this action-entry, you can define how they are delivered.
        You can use:
        - 'window': Makes a window, the user can directly take the items from. If the closes the window without having taken them all, they will land in a box. (DOESNT WORK)
        <BELOW 3 DOESNT WORK, but is planned, when possible>
        - 'box': Makes a 'drop box', with the item(s)
        - 'mobdrop': Makes a mob, that drops the item(s)
        - 'meteor': 'Will use the (escapepod) to deliver the items'
    
        - OR you may write a blueprint name, that exist on your server (BA_GhostAdvStealth for instance). Note that they have to be in the PREFAB DIR (as of a8.1). Specify the FILE NAME
        Note, that you CAN use this mod to spawn multiple structures. Use the item-interface, and simply use more actions. The intent however, is to have fewer structures, that are cleaned up on use, and can reward items/execute commands on de-coring. 
        But if you wish to do spawning, you can.
        By default, this will be spawned as a BASE. If you need other types, please see 1.13.25
    
       1.13.9 - MaximumAmountOfItems:   
        (Int, optional, default: '0' - defaults to amount of items on the list)
        Similar to 1.9s, this will determine the max-picks of items
       
       1.13.10 - MinimumAmountOfItems:   
        (Int, optional, default: '0' - no minimum)
        Similar to 1.9s, this will determine the min-picks of items
       
       1.13.11 - AllowDuplicates:   
        (Boolean, optional, default: 'false' - no duplicates)
        Similar to 1.9s, determine if an item can be picked several times or not (true = yes, false = no)
       
       1.13.12 - ItemReceipient:   
        (String, optional, default: 'target')
        Who is to receive the items from the list in 1.13.16?
        - 'target': the target of this action
        - 'player': the executing player
       
       1.13.13 - ItemTitle:   
        (String, optional, default: "Stuff from {PlayerName}")
        If using 'window' for 'ItemDeliveryMethod'(see 1.13.8), you can set a title for the top of the container-window that opens.
       
       1.13.14 - ItemDescription:   
       (String, optional, default: "From command {EntryTitle}. player {PlayerName} sent you these items.") 
       Please note that if a user closes the window with items still inside the window, they will loose the items.
       If using 'window' for the 'ItemDeliveryMethod' (see 1.13.8), you can set a custom description, that is shown under the title from 'ItemTitle' (see 1.13.13), but above the items.
       
       1.13.15 - ItemButton:   
        (String, optional, default: "I'm done")
        If using 'window' for 'ItemDeliveryMethod' (see 1.13.8), you can set a custom text for the 'close window' button.
       
       1.13.16 - Items:   
        (List, optional - remove tag if you don't want to use items)
        
        1.13.16.1 - Item:   
         (String, required)
         Which item should be given. If vague, will pick last match. Recommended to use actual key. (for instance 'ironingot')
        
        1.13.16.2 - Amount:   
         (Int, optional, default: '1' (1 = 1 item)
         Amount of the items involved in the function
        
        1.13.16.3 - ContainPercent:   
         (Int, optional, default: '100' - high chance)
         How likely is it that this will be picked for the min-max.
    
        1.13.16.4 - Slot:
         (Int, optional, default: '-1')
         Can be used to specify which slot (in the inventory), the item will appear in. Please note, that it will override any item already in that slot, meaning that if you have two entries using the same slot, the last-selected will get it. Furthermore, if you have a mix of using slot, and not, the result might be a little random:
         When picking the slot, those without a slot specified (-1), will pick first availible.
         Say you have one with Slot: 0, and one without slot (-1). If the entry with slot:0 is picked first, both items will appear (it will be placed in slot 0, then when second entry looks for 'first-availible', it will pick slot 1). However, if the entry without slot (-1) is picked first, it will pick 'first-availible', which in this case is 0. When the second entry is then picked (that with fixed slot, 0), it will overwrite what was already picked for slot 0, and hence the other entry will effectively not exist.
         This is a little bit of randomness, introduced by the nature of having 'weighted random picks', which items are. You can use this to your advantage. However, if you want to make sure the player receives all items picked, you should either use fixedslot for all, or none.
         *Please note, that it will not respect the max-size for stacks, and may lead to false positives for EAH or other anti-cheat, if you put a stack to be larger than what is configured in your config.ecf file.
    
        1.13.16.5 - Special:
         (String, optional, default: 'NA')
         If you wish, you can chain several items into being added, using the syntax of <>
         <> being one or several of "[(<1>)(<2>)(<3>)]"
          <1> : The slotid to modify (0 indexed (slot 1 is 0))
          <2> : The item you wish to put there. Note, that it must be a valid ID, or 0.
          <3> : The amount of the given item you want to put there.
         For instance [(3)(432)(4)], will put a 4 RocketLaunchers in slot 4. (3)
         Note, that this still respects if the entry is picked at all, but can be used to make some crazyness.
         If slotid is specified to -1, it will pick 'first availible', as described in 1.13.16.4.
         Note that unlike 1.13.16.1, the <2> param (item), MUST BE THE ITEMS ID!
         Note, that if you want to use this tag, you MUST provide a valid 1.13.16.1, for instance '432'. The data from 1.13.16.1, 1.13.16.2 and 1.13.16.4 WILL BE IGNORED!
         *Please note, that it will not respect the max-size for stacks, and may lead to false positives for EAH or other anti-cheat, if you put a stack to be larger than what is configured in your config.ecf file.
    
        1.13.16.6 - Decay:
         (String, optional, default '0')
         When sending an item, do you wish to specify the items durability/decay?
    
        1.13.16.7 - Ammo:
         (String, optional, default '0')
         When sending an item, do you wish to specify the items ammo?
       
       1.13.17 - ItemReturn:
        (String, optional, default "NA")
        If you want to hook on the return on ItemExchangeWindows (Used with 1.13.8 'window'), you can use this.
        You should specify a command, and pass the returned data as variables. See example "InspectPlayer" and "InspectPlayerHelper" (default commands).
        You can use
        {exchangedata} : To get a [(<1>)(<2>)(<3>)] list of the returns, 
          <1> : The slotid to modify (0 indexed (slot 1 is 0))
          <2> : The item you wish to put there. Note, that it must be a valid ID, or 0.
          <3> : The amount of the given item you want to put there.
        (You can also use {exchangedataToolbar}, which splits the first 9 slots from the returned data, aswell as {exchangedataBag}, which removes the first 9 slots, and sets all slots minus 9.. These are only really usefull, if you are emulating a players inventory in the itemexchange, but not much more past that).
    
       1.13.18 - ItemXYZ:   
        (String, optional, default "0,0,0")
        If you are using a blueprint, a meteor, or a box, in 'ItemDeliveryMethod' (see 1.13.8) you should specify where you want these to drop.  By default, it will use coordinates 0,0,0
        You may specify one, two or three numbers, seperated by commas. No spaces - "30", "20,33" or "9,2,40".
       
       1.13.19 - ItemPlayfield:   
        (String, optional, default: targets' playfield)
        You should specify this if you are using blueprint, a meteor, or a box in 'ItemDeliveryMethod' (see 1.13.8).
        This is which playfield the 'ItemXYZ' coordinates (see 1.13.18) applies to where it's spawned.
       
       1.13.20 - ItemCustomName:   
        (String, optional, default: whatever the blueprint has)
        You can use this to specify a name to give the spawned object in-game.
       
       1.13.21 - ItemFaction:   
        (String, optional, default: 'alien')
        You may use:
        - 'alien': Will set the spawned thing to faction alien
        - 'admin' : Will set the spawned thing to faction admin
        - 'predator' : Will set the spawned thing to faction predator
        - 'prey' :  Will set the spawned thing to faction prey
        - 'player': Will set the spawned thing to the executing players faction
        - 'target': Will set the spawned thing to the target players faction
        - 'player_public' : Will set the spawned thing to players faction and set it to 'public'
        - 'player_private' : Will set the spawned thing to players faction and set it to 'private'
        - 'target_public' : Will set the spawned thing to targets faction and set it to 'public'
        - 'target_private' : Will set the spawned thing to targets faction and set it to 'private'
        - 'private' : Will set it to 'private' faction 0
        - 'public' : Will set it to 'public' faction 0
        You can otherwise specify what you want it to be set to manually. Note, that you must provide both parameters, otherwise your input is ignored, and it will default to whatever the 'target' have. You need to specify factionId,factionGroup : For instance if you want to bind it to a player (personal), who is of origin 1, and the players entityid is 3070, you would type "3070,1"
       
       1.13.22 - GrantRep:   
        (int, optional, default: '0' - none)
        You can use this to give the user rep, or remove rep, when this commands run, if you so desire.
    
       1.13.23 - For:
        (List, optional, remove if not used)
        This is the first thing executed in the Action (technically before 1.13.1), but is placed down here, as it can be confusing, and is not vital for anything, but rather for advanced use.
        You may use this tag to loop over a list of variables, and do something.
        ('poor mans' ForEach or For)
    
        1.13.23.1 Min:
         (int, optional, default: '0')
         What should the minimum value be, that is used? 
         If the list is a for loop, this would be
          for(int i=<this>;i<max;i++)
         If the list is a foreach, this would be the first that is actually used
          if(<min, discard)
    
        1.13.23.2 Max:
         (int, optional, default min or length of split list from 1.13.23.5)
         What should the maximum value be, that is used? 
         If the list is a for loop, this would be
          for(int i=min;i<<this>;i++)
         If the list is a foreach, this would be the max that is actually used
          if(>max, discard)
    
        1.13.23.3 Seperator:
         (string, optional, default '')
         If you intend to do a foreach array (pass something in 1.13.23.4), you should set this to the character(s) to split by.
         This could for instance be ',' ' ' or something else.
    
        1.13.23.4 Input:
         (String, optional, default 'NA')
         If you use 1.13.23.3, this is relevant, otherwise this does nothing.
         This is the input, that you are looking for 1.13.23.3 in
         (for instance, if this is 'hello this is icecream', and 1.13.23.4 is ' ' (a space), it would iterate each of the entries, as a foreach (hello > this > is > icecream))
         In foreach, you may refer to the current iterated with "{this}"
    
        1.13.23.5 ForEntry:
         (List, optional, should be supplied, if 1.13.23 is used)
         No matter if it's a for-loop, or foreach, this is what should happen for *EACH* iteration.
    
         1.13.23.5.1 Conditionals:
          (List, optional)
          See 1.13.1 for more help on this (OR)
    
          1.13.23.5.1.1 Conditions:
           (List, optional)
           See 1.13.1.1 for more help on this (AND)
    
           1.13.23.5.1.1.1 Condition:
            (String, optional)
            See 1.13.1.1.1 for more help on this
            Remember that you may use {i}, to get current enum, and if it's a foreach, you may also use {this}, to get current iterated.
           	This could for instance be 'if current count is greater than 25'
           	"{{i}-25}>0" 
    
         1.13.23.5.2 ForLines:
          (List, optional)
          Given the logic from 1.13.23.5.1 was passed, this will happen
    
          1.13.23.5.2.1 ForLine:
           (String, optional)
           What should happen in this iteration.
           Remember that you may use {i}, to get current enum, and if it's a foreach, you may also use {this}, to get current iterated.
           This could for instance be 'set value to current i'
           "{outval={i}}" 
    
           If you want to break the forloop, you may add '{break}' to a ForLine (1.13.23.5.2.1), and it will stop the iterations. 
    
          1.13.23.5.2.2 - NestToEntry:   
           (List, optional - Remove tag if not in use)
           Do you want to nest to additional entries? 
           Please take care when using this. It can easily amount to A LOT of commands being run, depending on what you use for inputting to the 'for loop'
             
           1.13.23.5.2.2.1 - PassCommand:   
            (String, required)
            What command should be attempted to be executed (like if it were typed by the player ingame). You can, and should, use references (See spoiler 'string replacement' for more on this)
             
           1.13.23.5.2.2.2 - TriggerNestCooldown:   
            (Boolean, optional - default: 'false')
            This is unstable. In some cases it will work, most it will not do anything.
            If true, will trigger the cooldown for this nested entry.
            If false, will 'execute it for free' cooldown wise.
             
           1.13.23.5.2.2.3 - BypassNestCooldown:   
            (Boolean, optional - default: 'false')
            This is unstable. In some cases it will work, most it will not do anything.
            If true, will execute no matter if the nested entry is on cooldown for the player, or not. If false, will respect the cooldown.
             
           1.13.23.5.2.2.4 - IgnoreRequirements:   
            (Boolean, optional - default: 'false')
            This is unstable. In some cases it will work, but in most, it will not do anything.
            If set 'true', will ignore the Requirements section for the nested entry.
            If set 'false', will respect it (not execute it, if user don't meet requirements)
       
       1.13.24 - Request:
        (List, optional)
        If you require additional data, you can use this functionality to fetch it.
        Note, that only one request per type is active, so you really shouldn't spam it, as you won't get the data properly.
    
        1.13.24.1 - RequestType:
         (String, required)
         What kind of call are we making? See request at https://empyrion.gamepedia.com/Game_API_CmdId (for instance 'Request_Playfield_Stats')
         Currently, supports Request_Playfield_Stats. If you need something, request it!
    
        1.13.24.2 - ResponseType:
         (String, required)
         What kind of response are we expecting? See source from 1.13.24.1, this is the corresponding event. (For instance 'Event_Playfield_Stats')
    
        1.13.24.3 - RequestParams:
         (List, optional/required)
         If the request takes additional parameters, they should be filled here. Otherwise the return might not happen/be lacking.
    
         1.13.24.3.1 - Param:
          (String, required)
          What is the param named? (See the 'Param data' part. If it only takes one parameter, you can put whatever here)
    
         1.13.24.3.2 - Value:
          (String, required)
          What should the param have of value. For instance 'Tallodar', if we are asking for a playfield.
    
        1.13.24.4 - RequestReturn:
         (String, required)
         In order for the request to do anything, it needs somewhere to put the data. This is the command that will be executed, upon receiving the data, similar to 'NestToEntry', and others like that.
         To get the generated data you requested, please use {returndata}, as part of this command.
         It is recommended to encase in '', incase the data should contain spaces.
    
       1.13.25 - ItemBPType:
        (string, optional, default: '2' (base))
        If you are using a blueprint for 'ItemDeliveryMethod', you may need to set the type of it. Default expected is base. Might not work with others, but option is exposed..
        Other examples of types: BA=2, CV=3, SV=4, HV=5, AstVoxel=7
        You may optionally use 'BA', 'CV', 'SV' and 'HV' as keywords, for ease of use.
        (you may also spell it out if you wish, for instance 'capital vessel', 'capitalvessel','small vessel','smallvessel','hover vessel','hovervessel','base','structure','ast','astvoxel','asteroid','voxelastroid'. Numbers are also supported, but use with care)
    
       1.13.26 - ItemCommand:
        (string, optional, default: 'NA')
        If you are using a blueprint for 'ItemDeliveryMethod', you can opt to make it also execute a command, when it gets de-cored. Specify the command here.
    
       1.13.27 - ItemPersistBlueprint:
        (Boolean, optional, default 'false')
        If true, will not remove the blueprint, after loot have been paid out (/server is shutting down).
    
       1.13.28 - ItemROT: 
        (String, optional, default "0,0,0")
        If you are using a blueprint, a meteor, or a box, in 'ItemDeliveryMethod' (see 1.13.8) you may want to specify how it is rotated.  By default, it will use coordinates 0,0,0
        You may specify one, two or three numbers, seperated by commas. No spaces - "30", "20,33" or "9,2,40".
    
     1.14 - AttemptAtLogin:   
      (Boolean, optional, default: 'false')
      If true, will execute this entry (AttemptToRunCommand 1.15 should be specified!), when an user logs onto the server.
     
     1.15 - AttemptToRunCommand:   
      (String, optional - required if you use 'AttemptAtLogin' (see 1.14) or AttemptToRunEveryInterval (see 1.16))
      What command should be executed? It is recommended to use varibles (see spoiler 'string replacement')
     
     1.16 - AttemptToRunEveryInterval:   
      (Int, optional, default: '0' - never repeat)
      If specified, will execute this again after this amount of SECODNS.
      WARNING: This command can easily get out of control, as it can only be used with 'AttemptAtLogin'. Hence it will be executed EVERY n-SECONDS, for EVERY PLAYER online, unless later 'broken' by 1.13.7.27. Use with care.
      Note that a player can only have one of these running. Last-executed will take priority.
      If you do not want it to be executed immediatly as the player logs in aswell, use 1.28
     
     1.17 - Hooks:   
      (List, optional, remove if not used)
      You can use this to 'hook' commands up to ingame events. Refer to HookOn (see 1.17.1), as to which can be used for hooks. 
      Example - If *X* happens => execute *Y* command as if the player manually entered it themselves.
      
      1.17.1 - HookOn:   
       (String, required if you use Hooks (see 1.17))
       What detail do you want the mod to 'hook' onto.
       - 'gamestart': Will fire when the first player enters the game, since last server start. Might be usefull for setting some dynamic variables.
    
       - 'changedplayfield': Will fire when a player changes playfield. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {thisplayfield}: playfield changed TO, {previousplayfield}: playfield changed FROM (names of playfield).
    
       - 'firstonplayfield': Similar to 'changedplayfield', but will only fire the first time any player enters the playfield, since last server start. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {thisplayfield}: playfield changed TO
       
       - 'connect': Will fire when a player connects to the server. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid
    
       - 'disconnect': Will fire when a player connects to the server. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid-
       NOTE! This DOES NOT behave like a regular event.
       As the player is disconnected, we can not use the player as the 'executing party'. Therefore this will ONLY FIRE, if there's more people online.
       It will pick another (random) user that is online, and execute the event as this player.
       THEREFORE it is VERY IMPORTANT that you catch the {thisplayer} and/or {thissteam} to actually use this hook for anything!
       
       - 'playerdeath': Will fire when a player dies from non-pvp reasons (no other player involed). You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {deathtype}: which type of death (int). {deathtyperesolved}: will try to match the death event to a friendly sentence. See below. If you wish to know which entityid killed the player (if killed by something), use {entityinvolved}. If whatever killed you is operating an entity (player flying a HV for instance), you can use {operatingentity} to get this. You may use {secondssincelastdeath}, to determine how many seconds it have been since the player have last died. If no deaths this session, NA will be returned.
       
       - 'playerpvpdeath': Will fire when a player dies from pvp reasons (other player involed). You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {deathtype}: which type of death (integer). {deathtyperesolved}: will try to match the death event to a friendly sentence. See below. {otherplayer}: who was involved in the death (likely the killer). {othersteam}: The other players steamid. If whatever killed you is operating an entity (player flying a HV for instance), you can use {operatingentity} to get this. You may use {secondssincelastdeath}, to determine how many seconds it have been since the player have last died. If no deaths this session, NA will be returned.
       
       - 'playerpvpkill': Will fire when a player is involved in another players death. You may use {thisplayer}: triggering players entityid (killer), {thissteam}: triggering players steamid (these are likely the killers), {deathtype}: which type of death (integer). {deathtyperesolved}: will try to match the death event to a friendly sentence. See below. {otherplayer}: who died. {othersteam}: the steamid of the deceased. If whatever killed you is operating an entity (player flying a HV for instance), you can use {operatingentity} to get this.
    
       Various death types - Unknown = 0,Projectile = 1,Explosion = 2,Food = 3,Oxygen = 4,Disease = 5,Drowning = 6,Fall = 7,Suicide = 8).
    
        Note on deathtypes: (The resolved should be usuable as a 'was killed by <>' sentences)
        - 0 (or unresolvable): Unknown (Not known reason). Will resolve to 'Unknown means'
        
        - 1: Projectiles (shot or similar). Will resolve to 'Projectile'
        - 2: Explosives (Rocket / C4 / other explosion). Will resolve to 'Explosive'
    
        - 4: Oxygen (Ran out of air / drowned). Will resolve to 'Oxygen deprevation'
    
        - 7: Fall damage. Will Resolve to 'Falling'
    
        - 10: Entity (melee?). Will resolve to 'Creature'
       
       - 'coreadded': Will fire when a player ADDS a core to a structure. Note that the structure can be previously owned by the player or not. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {onentity}: the entity id of the structure that it was placed on.
       
       - 'coreremoved': Will fire when a player REMOVES a core to a structure. This can be both from damage, or multitool. Note that the structure can be previously owned by the player or not. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {onentity}: the entity id of the structure that it was placed on.
       
       - 'poweron': Will fire when a player turns the power ON for a structure. Note that this can be spammed like crazy. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {onentity}: the entity id of the structure that was turned on.
       
       - 'poweroff': Will fire when a player turns the power OFF for a structure. Note that this can be spammed like crazy. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {onentity}: the entity id of the structure that was turned off.
       
       - 'pdachapteractivate': Will fire when a player activates a PDA chapter. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {chaptername}: the KEY for the chapter that was activated.
       
       - 'pdachapterdeactivate': Will fire when a player activates a PDA chapter. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {chaptername}: the KEY for the chapter that was deactivated. (Keep in mind, that completing a chapter will ALSO send this)
       
       - 'pdachaptercomplete': Will fire when a player activates a PDA chapter. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {chaptername}: the KEY for the chapter that was completed.
    
       - 'chat': Will fire whenever a chat message is typed. You may use {thisplayer}: triggering players entityid, {thissteam}: triggering players steamid, {chatmessage} for the message typed, {chattype} for the type of message sent (faction/global/..)
    
       - 'LeaveZone': Will fire whenever a player enters within a defined zone, defined by 1.17.3. You may use {thisidentifier} : The tag from param 'identifier', {thisplayfield} : The playfield this was bound to, {thistype} : The number of event (EnterZone=1, LeaveZone=2, InZone=3), {thisx} : param 'x', {thisy} : param 'y', {thisz} : param 'z', {thisx2} : param 'x2', {thisy2} : param 'y2', {thisz2} : param 'z2', {dynamic} : Was this started from code (true), or YAML (false)
    
       - 'EnterZone': Will fire whenever a player leaves a defined zone, defined by 1.17.3. You may use {thisidentifier} : The tag from param 'identifier', {thisplayfield} : The playfield this was bound to, {thistype} : The number of event (EnterZone=1, LeaveZone=2, InZone=3), {thisx} : param 'x', {thisy} : param 'y', {thisz} : param 'z', {thisx2} : param 'x2', {thisy2} : param 'y2', {thisz2} : param 'z2', {dynamic} : Was this started from code (true), or YAML (false)
    
       - 'InsideZone': Will fire while a player is in a defined zone, defined by 1.17.3. You may use {thisidentifier} : The tag from param 'identifier', {thisplayfield} : The playfield this was bound to, {thistype} : The number of event (EnterZone=1, LeaveZone=2, InZone=3), {thisx} : param 'x', {thisy} : param 'y', {thisz} : param 'z', {thisx2} : param 'x2', {thisy2} : param 'y2', {thisz2} : param 'z2', {dynamic} : Was this started from code (true), or YAML (false)
    
       - 'FactionChange': Will fire when a player changes their factions. You may use {oldfaction} and {newfaction} to get the ID of the old, and new faction. (Old being what was changed from, and new, what was changed to). If previously not in a faction, will return -1
    
       - 'ItemSold' : Will fire when a player have completed a trade with a trader. You may use {boughtitemamount} to get the amount bought of the item, {boughtitem} to get the bought items ID, {boughtitemprice} to get the price the item was bought at (Total or single?), {boughtitematstructureid} to get the structure ID it was bought at, {boughtitemattraderid} to get the ID of the trader, {boughtitemattradertype} to get the type of the trader.
    
       - 'ValutaChanged' : Will fire whenever a valuta changes (reputation, currencies, etc). You may use {valuta} (What valuta was affected), and {amount} (With how much was it changed). Additionally, you may use {oldval} and {newval} to get what it used to be, and what it is now.
    
       (Note, that you are technically also able to hook on a player closing an ItemExchange window. This has to be done by 1.13.17 tho, as it must be connected to an initiating event to make sense)
      
      1.17.2 - HookCommand:   
       (String, required if you use 1.17)
       Which command will fire, when the hook defined in 1.17.1 is met.
       Note the different params allowed, to pass specific data, from 1.17.1, depending on what is hooked.
       (This is effectively the same, as if the player typed 'commandname commandparam1 (...)').
    
      1.17.3 - HookParams:
       (List, optional)
       Depending on the 1.17.1, it might expect additional params to trigger, such as 'InsideZone', 'EnterZone', 'LeaveZone'
    
       1.17.3.1 - ParamName
        (String, required if using 1.17.3) 
        Depending on the 1.17.1, you might be able to use params:
        For
        - 'EnterZone', 'LeaveZone', 'InZone' : 
          These expects an area defined by two XYZ pairs.
          - 'identifier': (Recommended, string) When you get a callback, this will be mentioned. Might be helpfull.. Can also be used to Remove/Delete the zone dynamically.
          - 'x': (Recommended, int) Where does the X start? Default 0
          - 'y': (Recommended, int) Where does the Y start? Default 0
          - 'z': (Recommended, int) Where does the Z start? Default 0
          - 'x2': (Recommended, int) Where does the X end? Default x
          - 'y2': (Recommended, int) Where does the Y end? Default y
          - 'z2': (Recommended, int) Where does the Z end? Default z
            (Note: For x2, y2, z1, you may do +100 or -100 (100 could be any number), if you want to do the size relative to the x, y, z)
          - 'playfield': (Required, string) Which playfield is the pair bound to? 
            NOTE playfield IS case sensitive
          - 'interval': (Optional, int, seconds, min. 1. Default 60) How often should the playfield be checked for updates? Note, if you have multiple on the same playfield, the one with the shortest interval counts for all (So if you have one set to 1 and one to 5, it will check every 1). Note, that this is no matter the type, they all stack to the shortest.  
          (Note: If x, y, z and x2, y2, z2 is not specified / set to the same value, or a playfield is not specified, the hook willbe ignored. Further note, that the trigger will run IMMEDIATLY for the first player entering a playfield, and then every interval after that. The trigger will stop, if no players is on the playfield)
    
       1.17.3.2 - ParamValue
        (String, required if using 1.17.3.1)
        What value should the param take?
     
     1.18 - DisallowManual:   
      (Boolean, optional - default: 'false')
      If true, will disallow manually triggering the command (ie. it must have originated from a hook, or similar ; Can't be from console/chat).
    
     1.19 - SuppressExecNotice:
      (Boolean, optional - default: 'true')
      If false, will output a text to the executing user, telling them the command was executed successfully.
      (The line will read "Server: <CustomName>: Executing command <EntryKey>", and will be sent only to the player that 'typed' the command).
    
     1.20 - HideUntillUsed:
      (Boolean, optional - default: 'false')
      If true, will not list the command on the list of availible (or locked) commands, untill the user have used it. Can be usefull, if you want the user to 'learn' new commands, through exploration or whatnot.
    
     1.21 - HideSpent
      (Boolean, optional - default: 'true')
      If true, will hide the command from the list, if it requires charges (1.3), and the user have none, or that it have a limitmaximumuses (1.2), and the user have used it up to this limit. Can be disabled if you for whatever reason wants it shown, but it is hidden from the list by default, if it cannot be used because of the above mentioned.
    
     1.22 - RequireActionsToTakeCost
      (Integer, optional - default: '0')
      How many actions (1.13) must be successfully executed (ie. conditionals passed), in order to charge the user. Default is none (charge if it was allowed to execute any actions at all).
      NOTE that this number is ONLY counted up for actions that DOES NOT have a delay set (1.13.2 = 0)
    
     1.23 - DisallowHelp
      (Boolean, optional - default 'false')
      If true, will not show help for this command, if requested.
    
     1.24 - HelpText
      (String, optional - default "NA")
      If set, will be displayed in the help command, as the second line. This text could be used to describe what the command is supposed to do (description), or offer additional guidance.
      NOTE that you CANNOT use any {} references in this entry!
    
     1.25 - Version
      (double, optional - default "1.0")
      If set, will display the version number in the help command.
      The primary purpose of this, is to help entry-creators keep track of wether a problem is because of the entry not being updated (running old version), or if there's an actual issue.
      The entries should start at '1', for the first public release, and increase however you like. Note, that the format must be '1.1', '1.2', '3.774' etc. How you increase them is up to you, but you should increase the number, every time you make a change to it.
    
     1.26 - Function:
      (List, optional)
      If you want, you can specify one or several 'functions', that can be invoked with stringreplace {Function(..)} (read more under stringreplace). These can only be invoked under *this* entry.
      The basic premesis is that you pass 0-x variables to it, and it will resolve to something else.
      You can use this to nest forloops aswell (please use this with care!).
    
      1.26.1 - FunctionName:
       (String, required)
       What should be used to invoke this function? Can be something as generic as 'function1' if you want.
    
      1.26.2 - DefaultReturn:
       (String, optional, default "")
       What should the default return of the function be (if nothing else is specified for {returnval} during the function, this will be returned)
    
      1.26.3 - For:
       (List, optional (tho recommended))
       Please refer to 1.13.23 (For).
       This section can do everything that you can do with a normal forloop.
    
      1.27 - Creator:
       (string, optional)
       Can be used to sign your work. Doesn't actually do anything ingame.
    
      1.28 - AttemptToRunEveryIntervalSkipLogin
       (Boolean, default: false)
       Should the first time be skipped? (Default is 'no' (false))
       Really only makes sense to use with 1.16.
       Allows you to do 'wait 1600 seconds, and THEN do it every 1600 seconds', rather than 'do now, and then every 1600 seconds'.
    
    Additional Functions: 
    In addition to the Entries, the config file also consist of the following funtions.
    
    2 - OutputAllToLog:   
     (Boolean, required)
     If true, will output (a lot) of debug text to the log. You should use this if something doesn't work as you expect (can be used to debug 'Conditionals' for instance). Also required to obtain help.
    
    3 - NameSpace:   
     (String, required)
     In addition to an entry's normal 'i:0', i:0 becomes i:1, i:1 becomes i:2 if this is used, and so on), or if no i:0 is specified, this will be required to use the command.
     If not set as 'i:0', the user will have to type '!<namespace> i:1'. 
     If 'i:0', the user can type '!<namespace> i:0', if desired.
     Typing the namespace alone will give a list of all commands (locked or availible). If command requires admin, it will be hidden entirely from non-admin users.
     To reload the config file, type '!<namespace> rld cnf'
     The namespace should be something short for ease of use, such as 'cc'.
     Please note that the namespace should not be the same as another mod's namespace to avoid any conflicts.
    
    4 - CustomName:   
     (String, required)
     If you want the ingame-replies to have a specific color, or to be called something other than 'CommandCrate', you can change that here. See spoiler 'Make customname have color' for more info.
     
    5 - SpecialsFile:   
     (String, required)
     Where is the specials file located (SpotGuards specialsfile.yaml (by default)). See spoiler "Share the file(s) with another mod" for more info on paths.
     Example - If you have installed SpotGuard as per default, it would be "../SpotGuard/specialslist.yaml"
    
    6 - SpotGuardConfigFile:   
     (String, required)
     Where the spotguards config file located (SpotGuards config.yaml, by default). See spoiler "Share the file(s) with another mod" for more info on paths.  If you have installed SpotGuard per default, it would be "../SpotGuard/config.yaml"
    
    7 - LastUsedFile:   
     (String, required)
     Where the lastused file is located. See spoiler "Share the file(s) with another mod" for more help on paths if you don't want it in this mods directory.  If you have installed default, it would be "lastused.yaml"
     
    8 - PermaDataFile:   
     (String, required)
     Where the permadata file is located.  See spoiler "Share the file(s) with another mod" for more info on paths, if you don't want it in this mods directory.  If you have installed default, it would be "permadata.yaml"
    
    9 - ValutaFile:
     (String, required)
     Where the valuta file is located. See spoiler "Share the file(s) with another mod" for more info on paths, if you don't want it in this mods directory.  If you have installed default, it would be "valuta.yaml"
    
    10 - ValutaHelperFile:
     (String, required)
     Where the valuta file is located. See spoiler "Share the file(s) with another mod" for more info on paths, if you don't want it in this mods directory.  If you have installed default, it would be "valutahelper.yaml"
    
    11 - AllowShort:   
     (Boolean, required)
     If an entry is using 'TargetAccept', the user must '/accept' or '/deny'.  If AllowShort is 'true', it will allow them to type it directly as '/accept'. If it's false, it requires the namespace infront. Chat Entry Example - '/<namespace accept'
     Set to 'false' if you have multiple mods using '/accept', '/deny', '/allow', or '/disallow' (as this may cause conflicts if not taken into consideration. This mod will only react to the command if a user have a pending request from an entry.
     
    12 - AllowServer:   
     (Boolean, required - default: 'true')
     If true, the mod will run an API server that will convey commands to the mod.  This would allow an admin to execute entries via web browser, without having to do it ingame.  Refer to spoiler about API for more on this
    
    13 - ServerPort:   
     (Int, required - default: '8089')
     Specify which port you want the API server (see 12) to run on?
     If you want access from outside your computer/network, you must portforward this.
    
    14 - ServerAPIkey:   
     (String, optional - default: 'NA')
     If sepecified, will require this to be passed along with any requests, in order for the mod to react to it. (basically a password.. you shouldn't expose this to anyone)
    
    15 - ServerSaveLogfileDir:   
     (String, optional - default: 'server')
     This setting will save a logfile with all the incoming events, including denied ones, in this directory. If no logfile is desired, set the value to 'NA'. Setting string value to 'server' would mean /server/logfile)  The naming of the logfile is day-month-year-minute-second.log of when the API server was started.
    
    16 - MasterAPIkey:   
     (String, optional - default: 13-ServerAPIkey)
     If you have multiple mods, using the same 12 - serverport, they will collide. This mod is compatible with SpotGuard in sharing ports however. You should specify the MasterAPIkey to whichever mod becomes the master (is run first - usually the one in the directory that comes first alphabetically, so it depends on what you have named the folders. If you use the default names for the folders, it would be this mods key).
    
    17 - BackupFilesAt:
     (String, optional - default: 'NA')
     If you want, you can specify a folder, where a backup of the files
     - lastused.yaml
     - permadata.yaml
     - valuta.yaml
     - valutahelper.yaml
     will be created, every time the server starts, and stops.
     Sometimes, if you experience a crash, or similar, it might be during a write-cycle, and a file might end up corrupted. These backups are to help this, should it happen, to atleast only be a rollback to latest server start/stop.
     Note, that there is no 'deletion mechanic'. If you want the files gone, you have to delete them.
     If set to anything but 'NA', will create a directory of that name, and store the backups there.
    
    18 - AdminRank:
     (Int, optional - default: '3')
     If you want to limit admin commands, to only be executable by a rank and higher, specify it here.
     Commands effected by these, are the hardcoded ones:
     -cc reload
     -cc charges
    
    19 - DefaultLanguage:
     (String, optional, default: 'English')
     Reference to Localization.csv entry.
     Which language should be picked, if user havn't specified one/none was found.
    
    20 - PermaDataDirectory
     (String, optional, default: 'permadata')
     Which folder do you wish to have additional permadata saved to?
    
    
    Feel free to copy the above into a texteditor of your choice. This will make reading it considerably easier.
     
    #4
    Last edited: May 15, 2019
    Ephoie likes this.
  5. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    First of all: An entry is a command. Each entry makes something happen.
    You can refer to the above documentation, together with the demo file 'configentrytemplate.yaml', for how your entry should be structured.

    But then the question remains: Where do you put it?
    You have multiple options for this:
    -You can open the config.yaml, and append it to the Entries: tag
    *This is not recommended, as it will make it harder for you to update the mod in the future.
    -You can add a new .yaml file to the mods directory, for instance, you can call it 'votekick.yaml'.
    Inside this, you MUST add a line
    Code:
    Entries:
    After this line, you would put your data, as if you had put it inside the config.yaml itself.
    -You can add a new .yaml file in the /additional/ folder.
    If you prefer, you can do the exact same thing as above, but in this folder. This is to keep the folder cleaner, if you so prefer. Please do not create your own folder, as it will not read the file. Make sure, that the file is located as /additional/votekick.yaml (or whatever name you give it rather than 'votekick'), relative to the mod folder.
    -You can add any additional folders under /additional/.
    You can, like with files, uncomment the folder by adding a # infront of the filename. This will ignore its content.

    In addition to adding multiple .yaml files, you may add additional .csv files.
    For the same reason, as described in the first point above, it is not recommended to add more lines to the 'Localization.csv' file. Rather, you should create your own.
    You may do exactly the same as above, but instead of having a .yaml extension, it must have a .csv extension.
    And instead of having a line 'Entries:', it must have a valid format at the top:
    For instance:
    Code:
    KEY,Deutsch,English
    TestString2,Sehr gut,This is a test
    You can also add .valuta.yaml files. These must follow the format of the 'valutahelper.yaml' file. You can use these to inject custom valuta information. (To go with the theme of being easy to share, you should put required reputations into a .valuta.yaml file, that you share aswell)

    You can additionally add .data.yaml files. These files must follow the format of the 'permadata.yaml' file. These files are a little special however, compared to the others.
    These files will not be *injected*, but rather, *copied to* the /permadata/ folder.
    If a file with the same name exist, it will by default not be copied. However, you can add 'AlwaysOverwrite: true' to the file, to make sure it always gets overwritten.
    This check will only be performed whenever the server starts! (each time it is started).
    Example - Will always overwrite the file, if already exist in /permadata/. Don't use 'AlwaysOverwrite: true', if you don't want this to happne:
    Code:
    AlwaysOverwrite: true
    Entries:
    - KeyRef: test2
      Data: this is testX2
    The purpose of this, is to allow for a config file to exist, with default values, that can easily be changed. (Configs for your command)
    Note, that you can optionally specify it to being saved to a file, rather than the main one, with using 1.13.3.3

    You can finally add .epb files. These will be copied into the servers <serverroot>/Content/Prefabs folder, so that they can be spawned from the mod.

    Note: If a key collide with another, it will be skipped. Make sure to use an unique key.
    Additionally, the order you put it in, doesn't have to align with the original Localization.csv.
    -If your file contains additional languages, it will simply be 'added' to the memory.
    -If your files order is different than the main Localization.csv, it will be automatically 'rearranged' in the memory.
    (So if main file is
    Code:
    KEY,English
    TestString,This is a test
    And your file is
    Code:
    KEY,Deutsch,English
    TestString2,Sehr gut,This is a test
    It will automatically fix it, so the Deutsch is 'added', and the English actually gets returned to English speakers (etc)).

    (Remember, to actually make use of the translations, to use the {Lang:key} tag, as described in-depth in String-replacement)

    For convenience, you can uncomment an entire file, by prepending it with a #
    (For instance '#votekick.yaml', would make the file votekick.yaml be ignored, and not imported)
    This goes for .csv files aswell, and folders.
    DO NOT use this with the Localization.csv, permadata.yaml, /additional/, lastused.yaml, valuta.yaml or config.yaml !!
    You should attempt not to use the mods directories, as they may change with any update. If you don't want to use the defaults, you should uncomment the directory, and create your own version of what you want to use.

    Anything in spotguards config file, can be referred to with the {configkey}, as you can in spotguards config file itself.

    For the below: You should always expect that they might not be resolved to anything, or blank.

    {TargetType}: Resolves to either blank, or the type of the target (what was used to 'get here') (entityid (from entity|entityid), playerid (from player|playerid), playername (from playername|playername+), steam (from steam|steamid), client (from client|clientid), poi (from poi|poispecial|poiall), playfield (from playfield), factionplayer (from faction), all (from all))
    {EntryTitle}: Resolves to the entry key for this entry

    {PlayersOnline}: Resolves to the current number of players online.
    {AdminsOnline}: Resolves to the current number of admins online.
    {AdminsList}: Resolves to a CSV list of online admins, steamIDs
    {PlayerList}: Resolves to a CSV list of online players, steamIDs

    {SteamToEntity(<1>)} : Attempts to resolve a steamid to an entityid. Will return -1, if none found.
    {EntityToSteam(<1>)} : Attempts to resolve an entityid to a steamid. Will return -1, if none found.

    {PlayerName:<>} : Resolves to the player name
    {PlayerRank:<>} : Resolves to the players rank (0/3/6/9)
    {PlayerRankResolved:<>} : Resolves to the adminrank (same as {AdminRank} would, but based on a steamid, rather than the current executing player)
    {PlayerRankResolved_s:<>} : Resolves to a short adminrank (same as {AdminRank:s} would, but based on a steamid, rather than the current executing player)
    {VipRank:<>} : Resolves to the players VIP
    {VipRankResolved:<>} : Resolves to the viprank (same as {VipSpecial} would, but based on a steamid, rather than the current executing player)
    {VipRankResolved_s:<>} : Resolves to a short viprank (same as {VipSpecial:s} would, but based on a steamid, rather than the current executing player)
    {RepRank:<>} : Resolves to the players Rep
    {RepRankResolved:<>} : Resolves to the viprank (same as {RepSpecial} would, but based on a steamid, rather than the current executing player)
    {RepRankResolved_s:<>} : Resolves to a short viprank (same as {RepSpecial:s} would, but based on a steamid, rather than the current executing player)
    <> being a steamID

    #Language related:
    You can resolve an entry to the users specified language, given it exist in the Localization.csv file: If it doesn't it defaults to the English entry for the KEY.
    You resolve it like this:
    {Lang:TestString}
    If your KEY in localization.csv is 'TestString' (This would for 'English' resolve to 'This is a test', with the default provided file).
    Note that you can add additional keys, simply by adding it below the current entry, as such:
    KEY,English
    TestString,This is a test
    TestString2,This is another test
    (would add another KEY, 'TestString2' which would resolve to 'This is another test' for 'English')
    Note, that if the key was not found (or otherwise didn't get resolved), it will return a 'MISSING <>', <> being the KEY.

    #Timedate related:
    {dd-MM-yyyy}: Resolves to the current day-month-year (22-05-2018 for instance)
    {dd}: Resolves to the current day (22 for instance)
    {MM}: Resolves to the current month (05 for instance)
    {yyyy}: Resolves to the current year (2018 for instance)
    {hh:mm}: Resolves to the current minute-second (13:10 for instance)
    {mm}: Resolves to the current minute (10 for instance)
    {hh}: Resolves to the current hour (13 for instance)
    {ss}: Resolves to the current second (27 for instance)
    {timestamp}: Resolves to the current timestamp

    For the timedates above, you can also use
    {TargetCon_someformat}
    {PlayerCon_someformat}
    {TargetLastOnline_someformat}
    {PlayerLastOnline_someformat}
    , with "someformat" being replaced with 'dd','MM','yyyy','hh','mm','ss' (day, month, year, hour, minute, second), and 'dd-MM-yyyy','hh:mm' (being formatted to 'day-month-year' and 'hour:minute').
    They can also be supplied without the _someformat, to get their 'dd-mm-yyyy hh:mm' (day-month-year hour:minute).

    For players: (Who executed the command. Note, that if other types than 'a player' was used to 'get here', target / player might behave differently)
    {PlayerID} : Executing player's steamid
    {PlayerName} : Executing player's name
    {PlayerX} : Executing player's position for x-axis
    {PlayerY} : Executing player's position for y-axis
    {PlayerZ} : Executing player's position for z-axis
    {PlayerRotX} : Executing players' rotation for x-axis
    {PlayerRotY} : Executing players' rotation for x-axis
    {PlayerRotZ} : Executing players' rotation for x-axis
    {PlayerBodyTemp} : Executing player's Body temperature
    {PlayerBodyTempMax} : Executing player's Maximum Body Temperature
    {PlayerBP} : Executing player's BP status. Name of what is currently being produced.
    {PlayerBPRemaining} : Executing player's BP status' remaining time.
    {PlayerCredits} : Executing player's ingame cash (what is stored 'in the bank'. Doesn't count what is in inventory)
    {PlayerExp} : Executing player's Experience
    {PlayerFactionGroup} : Executing player's faction group
    {PlayerFaction} : Executing player's faction (id of faction)
    {PlayerFactionResolved} : Executing player's faction (name of faction (if it could be found))
    {PlayerFactionResolvedAbbrev} : Executing player's faction (name of faction (if it could be found))
    {PlayerFactionRole} : Executing player's role in their faction
    {PlayerFood} : Executing player's food
    {PlayerFoodMax} : Executing player's maximum food
    {PlayerHealth} : Executing player's health
    {PlayerHealthMax} : Executing player's maximum health
    {PlayerKills} : Executing player's amount of kills
    {PlayerOrigin} : Executing player's origin
    {PlayerOxygen} : Executing player's oxygen
    {PlayerOxygenMax} : Executing player's maximum oxygen
    {PlayerPing} : Executing player's ping
    {PlayerPlayfield} : Executing player's playfield (currently on)
    {PlayerRadiation} : Executing player's radiation
    {PlayerRadiationMax} : Executing player's maximum radiation
    {PlayerStamina} : Executing player's stamina
    {PlayerStaminaMax} : Executing player's maximum stamina
    {PlayerStartPlayfield} : Executing player's starting playfield
    {PlayerUP} : Executing player's upgrade points
    {PlayerPermission} : Executing player's permissions (admin). Usually '0', if none is given
    {RepSpecial} : Executing player's resolved reputation (long) (from spotguard config)
    {RepSpecial:s} : Executing player's resolved reputation (short) (from spotguard config)
    {PlayerRep} : Executing players' reputation
    {PlayerCon} : Executing players' connection time (stamp)
    {PlayerEntity} : Executing players' entity id
    {PlayerClient} : Executing players' client id
    {PlayerVIP} : Executing players' VIP level (0-x)
    {VipSpecial} : Executing player's resolved VIP (long) (from spotguard config)
    {VipSpecial:s} : Executing player's resolved VIP (short) (from spotguard config)
    {PlayerAdmin} : Executing players' admin status (same as playerpermission, but what was resolved by the mod, rather than what was passed with the entity request. Should be the same tho)
    {AdminRank} : Executing player's resolved AdminRank (long) (from spotguard config)
    {AdminRank:s} : Executing player's resolved AdminRank (short) (from spotguard config)
    {PlayerOnlineTime} : In minutes, how long the Executing player have played on the server
    {PlayerOnline} : In minutes, how long the Executing player have been online this session
    {PlayerSecondsSinceLastDeath}: Will Resolve to how long it have been since the Player have died in seconds, or 'NA' if player havn't died this session

    {PlayerToolbar} : Returns a CSV of all item IDs, the Player have in toolbar.
    {PlayerBag} : Returns a CSV of all item IDs, the Player have in bag.
    {PlayerToolbarContains(<>)} : <> being an item id (int); Returns a CSV of all the toolbar inventory slots, that contains any amount of this item. ("1,5,6" for instance)
    {PlayerBagContains(<>)} : <> being an item id (int); Returns a CSV of all the bag inventory slots, that contains any amount of this item. ("1,5,6" for instance)

    {PlayerToolbarTotal} : Returns a [(<1>)(<2>)(<3>)] formatted, with <1> being slotid of item, <2> being item id and <3> being amount. May contain several of these. Returns the players toolbar.
    {PlayerBagTotal} : Returns a [(<1>)(<2>)(<3>)] formatted, with <1> being slotid of item, <2> being item id and <3> being amount. May contain several of these. Returns the players bag.
    {PlayerBagToolbarTotal} : Returns a [(<1>)(<2>)(<3>)] formatted, with <1> being slotid of item, <2> being item id and <3> being amount. May contain several of these. Returns the players toolbar and bag (slotid for bag is +9 of what they actually are).
    In addition, after (<3>), you can optionally specify (<4>)(<5>), <4>: decay/durability, <5>: ammo.

    To modify inventories directly:
    {ModifyPlayerBackpackSlots(<>)} : Will modify the Players backpack (bag).
    <> being one or several of "[(<1>)(<2>)(<3>)]"
    <1> : The slotid to modify (0 indexed (slot 1 is 0))
    <2> : The item you wish to put there. Note, that it must be a valid ID, or 0.
    <3> : The amount of the given item you want to put there.
    For instance [(3)(432)(4)], will put a 4 RocketLaunchers in slot 4. (3)
    {ModifyPlayerBackpackSlots([(3)(432)(4)])}
    If you want to modify several slots, you can do
    {ModifyPlayerBackpackSlots([(2)(431)(1)][(5)(430)(3)])}
    (etc).
    {ModifyPlayerToolbarSlots(<>)} : See above documentation. Same syntax and approach, but will modify the Players toolbar.
    Note that either syntax only modifies the slot specified, while leaving the remaining intact. (ie. if a player had a RocketLauncher in slot 1, and you modify slot2, player will still keep the RocketLauncher, as was)
    *Please note, that the modify will not respect the max-size for stacks, and may lead to false positives for EAH or other anti-cheat, if you put a stack to be larger than what is configured in your config.ecf file.
    In addition, after (<3>), you can optionally specify (<4>)(<5>), <4>: decay/durability, <5>: ammo.

    {PlayerInSlot<>}: <> being:
    '': Resolves to the ID of the item
    'Name': Resolves to the name of the item (note, the 'unfriendly' name (ie. 'ScifiCannonEpic' would be returned, if you have a 'Enhances Plasma Cannon' in that slot))
    'Amount': Resolves to the amount of items in that slot
    'Decay': Resolves to the decay value of the item in the slot (food/durability)
    'Ammo': If something is loaded into it, it will return here (For instance, gun with 9 shots loaded, should return '9')
    This is for the slots in the players *BAG* (ie. normal inventory)
    {PlayerInSlotToolbar<>}: <> being:
    '': Resolves to the ID of the item
    'Name': Resolves to the name of the item (note, the 'unfriendly' name (ie. 'ScifiCannonEpic' would be returned, if you have a 'Enhances Plasma Cannon' in that slot))
    'Amount': Resolves to the amount of items in that slot
    'Decay': Resolves to the decay value of the item in the slot (food/durability)
    'Ammo': If something is loaded into it, it will return here (For instance, gun with 9 shots loaded, should return '9')
    This is for the slots in the players *TOOLBAR*
    !Remember that the slots are 0 indexed (so slot 1 ingame, is 0 in code).

    If you want to know stats on usuage:
    {AmountUsed} : Returns how many times the player have used this command
    {ChargesLeft} : Returns how many charges the player have left on this command. Note that this will return 0, even if the command was successfull, in the cases where the command doesn't require charges to be used.

    For Targets: (Who executed the command. Note, that if other types than 'a Target' was used to 'get here', Target / Target might behave differently)
    {TargetID} : Target's steamid
    {TargetName} : Target's name
    {TargetX} : Target's position for x-axis
    {TargetY} : Target's position for y-axis
    {TargetZ} : Target's position for z-axis
    {TargetRotX} : Target's rotation for x-axis
    {TargetRotY} : Target's rotation for y-axis
    {TargetRotZ} : Target's rotation for z-axis
    {TargetBodyTemp} : Target's Body temperature
    {TargetBodyTempMax} : Target's Maximum Body Temperature
    {TargetBP} : Target's BP status. Name of what is currently being produced.
    {TargetBPRemaining} : Target's BP status' remaining time.
    {TargetCredits} : Target's ingame cash (what is stored 'in the bank'. Doesn't count what is in inventory)
    {TargetExp} : Target's Experience
    {TargetFactionGroup} : Target's faction group
    {TargetFaction} : Target's faction (id of faction)
    {TargetFactionResolved} : Target's faction (name of faction (if it could be found))
    {TargetFactionResolvedAbbrev} : Target's faction (name of faction (if it could be found))
    {TargetFactionRole} : Target's role in their faction
    {TargetFood} : Target's food
    {TargetFoodMax} : Target's maximum food
    {TargetHealth} : Target's health
    {TargetHealthMax} : Target's maximum health
    {TargetKills} : Target's amount of kills
    {TargetOrigin} : Target's origin
    {TargetOxygen} : Target's oxygen
    {TargetOxygenMax} : Target's maximum oxygen
    {TargetPing} : Target's ping
    {TargetPlayfield} : Target's playfield (currently on)
    {TargetRadiation} : Target's radiation
    {TargetRadiationMax} : Target's maximum radiation
    {TargetStamina} : Target's stamina
    {TargetStaminaMax} : Target's maximum stamina
    {TargetStartPlayfield} : Target's starting playfield
    {TargetUP} : Target's upgrade points
    {TargetPermission} : Target's permissions (admin). Usually '0', if none is given
    {TargetRepSpecial} : Target's resolved reputation (long) (from spotguard config)
    {TargetRepSpecial:s} : Target's resolved reputation (short) (from spotguard config)
    {TargetRep} : Targets' reputation
    {TargetCon} : Targets' connection time (stamp)
    {TargetEntity} : Targets' entity id
    {TargetClient} : Targets' client id
    {TargetVIP} : Targets' VIP level (0-x)
    {TargetVipSpecial} : Target's resolved VIP (long) (from spotguard config)
    {TargetVipSpecial:s} : Target's resolved VIP (short) (from spotguard config)
    {TargetAdmin} : Targets' admin status (same as Targetpermission, but what was resolved by the mod, rather than what was passed with the entity request. Should be the same tho)
    {TargetAdminRank} : Target's resolved AdminRank (long) (from spotguard config)
    {TargetAdminRank:s} : Target's resolved AdminRank (short) (from spotguard config)
    {TargetOnlineTime} : In minutes, how long the target have played on the server
    {TargetOnline} : In minutes, how long the target have been online this session
    {TargetSecondsSinceLastDeath}. Will Resolve to how long it have been since the Target have died in seconds, or 'NA' if player havn't died this session

    {TargetToolbar} : Returns a CSV of all item IDs, the target have in toolbar.
    {TargetBag} : Returns a CSV of all item IDs, the target have in bag.
    {TargetToolbarContains(<>)} : <> being an item id (int); Returns a CSV of all the toolbar inventory slots, that contains any amount of this item. ("1,5,6" for instance)
    {TargetBagContains(<>)} : <> being an item id (int); Returns a CSV of all the bag inventory slots, that contains any amount of this item. ("1,5,6" for instance)

    {TargetToolbarTotal} : Returns a [(<1>)(<2>)(<3>)] formatted, with <1> being slotid of item, <2> being item id and <3> being amount. May contain several of these. Returns the Targets toolbar.
    {TargetBagTotal} : Returns a [(<1>)(<2>)(<3>)] formatted, with <1> being slotid of item, <2> being item id and <3> being amount. May contain several of these. Returns the Targets bag.
    {TargetBagToolbarTotal} : Returns a [(<1>)(<2>)(<3>)] formatted, with <1> being slotid of item, <2> being item id and <3> being amount. May contain several of these. Returns the Targets toolbar and bag (slotid for bag is +9 of what they actually are).
    In addition, after (<3>), you can optionally specify (<4>)(<5>), <4>: decay/durability, <5>: ammo.

    To modify inventories directly:
    {ModifyTargetBackpackSlots(<>)} : Will modify the targets backpack (bag).
    <> being one or several of "[(<1>)(<2>)(<3>)]"
    <1> : The slotid to modify (0 indexed (slot 1 is 0))
    <2> : The item you wish to put there. Note, that it must be a valid ID, or 0.
    <3> : The amount of the given item you want to put there.
    For instance [(3)(432)(4)], will put a 4 RocketLaunchers in slot 4. (3)
    {ModifyTargetBackpackSlots([(3)(432)(4)])}
    If you want to modify several slots, you can do
    {ModifyTargetBackpackSlots([(2)(431)(1)][(5)(430)(3)])}
    (etc).
    {ModifyTargetToolbarSlots(<>)} : See above documentation. Same syntax and approach, but will modify the targets toolbar.
    Note that either syntax only modifies the slot specified, while leaving the remaining intact. (ie. if a player had a RocketLauncher in slot 1, and you modify slot2, player will still keep the RocketLauncher, as was)
    *Please note, that the modify will not respect the max-size for stacks, and may lead to false positives for EAH or other anti-cheat, if you put a stack to be larger than what is configured in your config.ecf file.
    In addition, after (<3>), you can optionally specify (<4>)(<5>), <4>: decay/durability, <5>: ammo.

    {TargetInSlot<>}: <> being:
    '': Resolves to the ID of the item
    'Name': Resolves to the name of the item (note, the 'unfriendly' name (ie. 'ScifiCannonEpic' would be returned, if you have a 'Enhances Plasma Cannon' in that slot))
    'Amount': Resolves to the amount of items in that slot
    'Decay': Resolves to the decay value of the item in the slot (food/durability)
    'Ammo': If something is loaded into it, it will return here (For instance, gun with 9 shots loaded, should return '9')
    This is for the slots in the players *BAG* (ie. normal inventory)
    {TargetInSlotToolbar<>}: <> being:
    '': Resolves to the ID of the item
    'Name': Resolves to the name of the item (note, the 'unfriendly' name (ie. 'ScifiCannonEpic' would be returned, if you have a 'Enhances Plasma Cannon' in that slot))
    'Amount': Resolves to the amount of items in that slot
    'Decay': Resolves to the decay value of the item in the slot (food/durability)
    'Ammo': If something is loaded into it, it will return here (For instance, gun with 9 shots loaded, should return '9')
    This is for the slots in the players *TOOLBAR*
    !Remember that the slots are 0 indexed (so slot 1 ingame, is 0 in code).

    If you need to convert an itemID into an unfriendly name:
    {NameOfItemID(<>)} : <> being an item ID (int). Will resolve to the unfriendly name (ie. 'ScifiCannonEpic' for instance).
    If you need the ID, and you have the unfriendly name or friendly name:
    {IDOfItem(<>)} : <> being the input, for instance 'shotgunshells' or 'shotgun shells'.

    {seed} : The current seed for the GAME (what is specified in dedicated.yaml)
    {save} : The current GameName from dedicated.yaml (ie where the savegame is)

    {isOnline(<ref>)} : <ref> being a steamid, checks if that steamid is online. Returns 0 if not, 1 if it is.

    {PlayersOnPlayfield(<1>)} : If you need to get all players on a playfield, you can use this. <1> being the playfield name. Note, that it returns the players ENTITYID in a CSV. (You can use it as a param for another command, that accepts steamids, if you want to use them as a target)

    {FactionsOnPlayfield(<1>)} : If you need all factions (IDs!) on a playfield, you can use this.
    <1> being the playfield name.
    Note that if any factionsless is on playfield, it will also return -1 for these.

    {Factions} : Returns a CSV list of all faction ids
    {FactionNames} : Returns a CSV list of all faction names
    {FactionName(<1>)} : Returns the name for a faction ID (1)
    {FactionAbbrev(<1>)} : Returns the name for a faction ID (1)
    {FactionMembers(<1>)} : Returns a CSV list of all members of the faction, by ID (1). Note the returned values are ENTITYIDs.
    Note, if supplied 0, it will return the same as {FactionLess}
    {FactionBelong(<1>)} : Returns the faction ID, that a player by ENTITY ID (1) belongs to.
    {FactionLess} : Returns a CSV list of all players that do not belong to a faction. Note the returned values are ENTITYIDs.
    Note, that PlayersOnPlayfield, and all the above faction ones only returns ONLINE PLAYERS.

    If you want to execute a command on a specific player (by steamid), you may use {QueCommand([><1><][><2><])}. If they are not online, the command will be executed next time they log on.
    <1>: the command to be executed. (with params)
    <2>: the steamid for the user.
    For instance {QueCommand[>.roll 10 100<][>84823432432<]} (84823432432 being an arbitrary steamid. Need to match a users ofcourse)

    You can interact with the valuta, which can be used for custom currencies, or reputation, or any other context, where you want 'something' with a 'standing'.
    You may use
    {ValutaAdd([<1>][<2>][<3>])}
    {ValutaSet([<1>][<2>][<3>])}
    {ValutaGet([<1>][<2>])}
    <1> : being the STEAMID of the user you want to get/modify.
    <2> : being the string of the currency/faction/..., referencing the 'name' in the valuta.yaml
    <3> : being the value you want to set/add
    It will be replaced with the new value for the entry.
    Default return is -99999 if an error occured, for instance, no entry to 'get' from.
    Example:
    {ValutaSet([{PlayerID}][TestFaction][23])} = Sets the value for 'TestFaction' to '23'
    {ValutaAdd([{PlayerID}][TestFaction][23])} = Adds '23' to the value of 'TestFaction'. Now '46'
    {ValutaGet([{PlayerID}][TestFaction])} = Return the value of 'TestFaction'. '46'.
    If we havn't created an entry for 'TestFaction2':
    {ValutaGet([{PlayerID}][TestFaction2])} = Return the value of 'TestFaction2'. Entry doesn't exist: Returns -99999

    {ValutaList} , or {ValutaList([<1>][<2>])}
    If param <1> is specified, will limit it to entries that have an entry in 'valutahelper.yaml', and this entry's 'typeof' matches <1>.
    If none is specified, will list all.
    Lists all valutas currently in use.
    If <2> is specified, will limit it to valutas in use by a certain user.

    {ValutaResolve([<1>][<2>][<3>])} : Resolves input to what is desired, based on <3>
    <1> : Being the valutas designation (for instance 'TestFaction')
    <2> : Being the value you wish to check. Either number, or reference to an 'Intervals' in 'valutahelper.yaml'
    (If you want to use the players amount of this valuta, resolve it with {valutaget} first, as explained above)
    <3> : Being how you wish the output returned:
    *0: Return corresponding name of lowest-matching-interval
    *1: Return corresponding value (startat) of lowest-matching-interval
    *2: Return <name>(<startat>)
    *3: Return <startat>(<name>)
    *4: Return how far into the tier we are
    *5: Return next tiers name
    *6: Return corresponding value for next tier (startat)
    *7: Return amount from next tier
    *8: Return distance from this tier to next
    *9: Return the description for the lowest-matching
    *10: Return the description for the next tier
    *11: Return the typeof for lowest-matching
    *12: Return the typeof for next tier
    *13: Initial value for this, with 'value' (<2>) being param for either startplanet, or origin. If it cannot resolve, it will try for -1 (if you have a '-1' value for OriginPlayfield or Origin, it will default to this).
    *14: Try to resolve it to a name (if you pass it a wrong casing, it will return a proper, if found in 'valutahelper').
    *15: Return the maxvalue allowed for this.
    *16: Returns the category for this.
    (For instance, if you type '0' in <3>, you will get 'Friendly' if that is a valid reference)
    For instance, if you use {ValutaResolve([TestFaction][9][0])} : It would resolve to 'Friendly', as this is the lowest matching (9). If you passed 10, upto '50', it would still resolve to 'Friendly'. 50 and onwards, it would resolve to 'Honored', as that is what the file describes.
    If no next-tier is met, 5 will return 'NA', 6 will return '-99999', 7 will return '0' and 8 will return '0'
    If not found, 0 will return 'NA', 1 will return '0', 2 will return 'NA(0)', 3 will return '0(NA)', 4 will return '-99999', and 5,6,7,8, will be unreliable.

    {MoveEntity([<1>][<2>][<3>][<4>])} : Can be used to move any entity (player or structure or.. ) to another position on same/other playfield.
    <1>: the entityid
    <2>: The new position in x,y,z (10,10,10 for instance).
    <3>: The new rot in x,y,z (50,50,50 for instance). Optional. Will get current rotation, if not specified.
    <4>: The playfield to do it on. Will default to players playfield if none specified. If playfield have space in name, please do NOT encase it in anything (simply write [Andromeda Galaxy] for instance)


    If you need data on POIs:
    {OnPlayfieldPOI(<1>)} : <1> being '' or 'all' or a playfield. Will display either all bases on playfield, or all bases 'datamined'.
    {OnPlayfieldPOISpecial(<1>)} : <1> being '' or 'all' or a playfield. Will display either all vessels on playfield, or all vessels 'datamined'.
    {OnPlayfieldPOIAll(<1>)} : <1> being '' or 'all' or a playfield. Will display either all bases&vessels on playfield, or all bases&vessels 'datamined'.
    They are listed seperated by a comma (CSV)
    Note, that the playfield you request for, must be generated by a player, in order to return anything.

    {EntityInfo([<1>][<2>])} : Get information on an entity (currently supporting 'player' and 'structure')
    <1>: The entityid
    <2>: See below- You can use either ID, or one of the 'alternatives'
    ID - works on - Alternatives - return
    0 - player/structure - name - Returns the name of the entity.
    1 - player/structure - type - Returns the type of the entity ('player' for player).
    2 - structure - pos - Returns the position of the entity. You can additionally use '2x' or 'posx' to get the X value, '2y' or 'posy' for Y, '2z' or 'posz' for Z. Default it will output z,y,z.
    3 - structure - rot - Returns the rotation of the entity. You can additionally use '2x' or 'rotx' to get the X value, '2y' or 'roty' for Y, '2z' or 'rotz' for Z. Default it will output z,y,z.
    4 - structure - factiongroup/fg - Returns the factiongroup of the entity.
    5 - player/structure - faction/factionid/fid - Returns the faction ID of the entity.
    6 - structure - core/coretype/co - Returns the coretype of the entity.
    7 - structure - class/classnr/size/classsize/cs - Returns the class size of the entity.
    8 - structure - blocks/cntblocks/countblocks/cb - Returns the count of blocks of the entity.
    9 - structure - devices/cntdevices/countdevices/cd - Returns the count of devices of the entity.
    10 - structure - lights/cntlights/countlights/cl - Returns the count of lights of the entity.
    11 - structure - triangles/cnttriangles/counttriangles/ct - Returns the count of triangles of the entity.
    12 - player/structure - playfield/plf - Returns the playfield of the entity.
    13 - structure - lasttouched/touched/lt - Returns the last touched time of the entity.
    ****You may format 13 as (by adding the first bit to the <2>)
    *_dd : Gets day (30)
    *_ddmm : Get day+month (30-12)
    *_mm : Get month (12)
    *_ddmmyyyy : Get day+month+year (30-12-2018)
    *_hhmm : Get hour+min (22:59)
    *_hh : Get hour (22)
    *_min : Get min (59)
    *_mmss : Get min+sec (59:48)
    *_hhmmss : hour+min+sec (22:59:48)
    *_ss : get sec (48)
    *_mmyyyy : get month+year (12-2018)
    *_ddmmyyyyhhmm : Get day+min+year+hour+min (30-12-2018 22:59)
    *_ddmmyyyyhhmmss : Get day+min+year+hour+min+sec (30-12-2018 22:59:48)
    (for instance <2> being '13_min' would resolve to '59')
    If nothing is specified with _, it will default to 'dd-MM-yyyy HH:mm:ss'
    ****Note, that all data may be up to 30m old. (Only gets updated every 30 minutes, so if anyone have touched it within this time, the data will be wrong)
    For instance, {EntityInfo([1044][12])} and {EntityInfo([1044][playfield])} and {EntityInfo([1044][plf])} would return, if 1044 is a player or structure, the playfield it is on.
    NOTE: if the entity is not within DSL (no player near enough to fully load), some of these values will return -1. Note, that coordinates are also unknown in this case, but will be '0,0,0' (or 0 for specific). Should be fixed with 8.3.
    It still knows: name,type,factiongroup,factionid,coretype,playfield.

    {DestroyEntity(<1>)} : Destroys the targeted entity. May be structure or npc
    <1>: The entityid of the thing you wish destroyed.
    (Destroy means removed. Using destroy to match the API)
    For instance {DestroyEntity(9043)} would remove this entity, if it was valid.
    Note: If you want to remove an entity (not structure):
    -If the entity is on the same playfield as the player, just leave it as-is
    -If the entity is on another playfield, you will have to specify what playfield it is located on. {DestroyEntity([<1>][<2>])}
    Here, <2>: is the playfields name. Casing doesn't matter.

    {RenameEntity([><1><][><2><])} : Renames the given entity to a new name.
    <1>: The entityid of the thing you wish to rename
    <2>: Being the new name you wish to rename it to.
    For instance {RenameEntity([>9043<][>Some cool new shiny name [Belonging to faction]<])}, would rename entity id of 9043 to 'Some cool new shiny name [Belonging to faction]'.
    Note that this *might* only work for structures.

    {SetEntityOwnership([><1><][><2><])} : Changes ownership of the given entity
    <1>: The entityid of the thing you wish to change ownership of
    <2>: Being the new name or abbrevation or id of the faction you wish to assign ownership to.
    For instance {SetEntityOwnership([>9043<][>factionname<])}, would rename entity id of 9043 to 'Some cool new shiny name [Belonging to faction]'.
    Note: Works on players & structures.
    Additionally supports 'Admin', 'Alien', 'Prey', 'Predator' (And their shorthand versions).
    Note that <2> is case insensitive.

    {POIsInRange(<1>)} : Returns the names of all POIs within this range. Based on the players position.
    <1> : being the range (number). Default is '500' if invalid, or less than 0
    Return is comma seperated list, if multiple results. Note, that it compares range as a rectangle. Uses x y and z.

    {POIsInRange_id(<1>)} : Returns the IDs of all POIs within this range. Based on the players position.
    <1> : being the range (number). Default is '500' if invalid, or less than 0
    Return is comma seperated list, if multiple results. Note, that it compares range as a rectangle. Uses x y and z.

    {POIsInRange_target(<1>)} : Returns the names of all POIs within this range. Based on the targets position.
    <1> : being the range (number). Default is '500' if invalid, or less than 0
    Return is comma seperated list, if multiple results. Note, that it compares range as a rectangle. Uses x y and z.

    {POIsInRange_target_id(<1>)} : Returns the IDs of all POIs within this range. Based on the targets position.
    <1> : being the range (number). Default is '500' if invalid, or less than 0
    Return is comma seperated list, if multiple results. Note, that it compares range as a rectangle. Uses x y and z.

    {ClosestPOI} : Returns the name of the closest POI. Can optionally be used as {ClosestPOI(<1>)}, with <1> being amount you wish (default <1> is 'hidden' set to 1).
    Note, that return is a CSV, if multiple results.

    {ClosestPOI_id} : Returns the ID of the closest POI. Can optionally be used as {ClosestPOI_id(<1>)}, with <1> being amount you wish (default <1> is 'hidden' set to 1).
    Note, that return is a CSV, if multiple results.

    {ClosestPOI_target} : Returns the name of the closest POI. Can optionally be used as {ClosestPOI_target(<1>)}, with <1> being amount you wish (default <1> is 'hidden' set to 1). Based on the targets position.
    Note, that return is a CSV, if multiple results.

    {ClosestPOI_target_id} : Returns the ID of the closest POI. Can optionally be used as {ClosestPOI_target_id(<1>)}, with <1> being amount you wish (default <1> is 'hidden' set to 1). Based on the targets position.
    Note, that return is a CSV, if multiple results.

    In addition to do 'static' hookOns (set in the .yaml files), you can do some more dynamic actions on them (edit them on the fly). You are able to add new, edit existing, or delete one.
    Note, that the tag is NOT resolved, so you can store it to memory, and use it at a game start command for instance.
    For the tags, please refer to 1.17.3.1, for the entry on "'EnterZone', 'LeaveZone', 'InZone'"

    {AddZone(<identifier>)(<type>)(<playfield>)(<x>)(<y>)(<z>)(<x2>)(<y2>)(<z2>)(interval)(command)} : Adds a hook to the temporary list. (If the identifier already exist, it will overwrite it). All params are required.
    {DeleteZone(<identifier>)} : Delete an existing zone.
    {EditZone[(<tag>)(<value)]} : Edits an exsisting zone. Several [] may be supplied, such as {EditZone[(identifier)(somevalididentifier)][(playfield)(akua)]}. Note that edits does not support chainging interval. (It does, but the change will only take effect, if lower than any other 'zone' hook on the playfield, and the playfield must be clear of players, before triggering). Note that it additionally does not allow you to change the identifier.
    NOTE, that the first param in edit MUST be a valid identifier, for it to change anything.

    You can additionally use any parameter used to create the command (commandentry), that have a 'key'.
    These can be accessed from {key}. For instance, entry:
    - i: 1
    type: playfield
    key: playfield
    Can later access the input for 'playfield', with {playfield}. If it was not specified, it will not resolve.

    You can also refer to 'permanent data', by using the key it was stored with.
    For instance, if you stored something by (under an action entry):
    - StoreData: #Store current playfield
    - RefKey: "originalPlayfield-{PlayerID}"
    Data: "{PlayerPlayfield}"
    You can later access it with {originalPlayfield-<steamid>}, <steamid> being the actual steamid of an player (as it was stored with {PlayerID}, which resolves to a steamid, as per the above).
    If nothing was found, it will not resolve.

    In addition to saving 'permanent data', you can overwrite, or save a new temporary variable.
    Temporary variables are initially set from the KEY in the commandentry part, but you can also manually write to them from anywhere.
    The variables exist for this 'session' (ie. while the server is running. Note that the variables are shared between players online, so you may want to use the players steamid as part of the name).
    You may do so, by using {variablename=value}.
    Note that you may use a-z 0-9 ,.!?#¤%&/()+-*$£½§<>. You MAY NOT use ' " = \ { [ ] }
    Here's some examples of what works:
    {helloworld=testy+test} : Saves "testy+test" to variable "helloworld"
    {helloworld3=123} : Saves "123" to variable "helloworld3"
    {helloworld=false} : Saves "false" to variable "helloworld"
    {HeLoWorld=12,3} : Saves "12,3" to variable "HeLoWorld"
    {helloworld=lorem=ipsum} : Fails, = is used twice.
    {helloworld={test{test}}} : Fails, {test{test}} is not resolved.
    {helloworld={d}{e}} : Fails, {d} and/or {e} is unresolved.
    {helloworld{d}=e{f}} : Fails, {d} and {f} is unresolved.
    {helloworld={testytest}} : Fails, {testytest} is unresolved.
    Also note, as shown above, that this means that unresolved references also is not allowed (meaning, that you may use variables, but the variables must resolve).
    If the statement is successfull (saves), it will replace the {} with the saved value (for instance, in example 2, where we set helloworld3 to 123, it would place '123', where you wrote it).
    You call these variables like usual, with {variablename} (example 2, to get '123', you would write {variablename}).
    NOTE that 'returnval' is a blacklisted word in this context, and will not be saved to!
    You can overwrite these as you desire.
    You can, in addition to just write it anywhere, use the tag 'variable' under 'Condition', 'Actions' and 'ExecuteCommand'
    Note, when referencing variables saved by 1.13.3, and are worried for collisions, when querying for data, and have it saved in another file, than 'permadata.yaml', you may do {filename:reference}, rather than just {reference}. If saved in "bounty", and your key is "status", you can either do {status} or {bounty:status}. If you have a reference for 'status' in multiple files, and reference it as {status}, it will be first-found used.

    Finally, all of the above is subject to MATH:
    If what you have written resolves to numbers, it will attempt to do MATH on it, if you have brackets around it:
    (For instance, if your string reads: "{{PlayersOnline}*2}", it will try to resolve {PlayersOnline}. If this becomes a number (for instance 3), it will read "{3*2}" which is a valid math string. This will then resolve to 6 (3*2=6)).
    You may use 'pi' as a keyword.
    This supports basic expressions such as +, -, *, /, %, sqrt, cos, sin, ^, and (atleast mostly), correct order, by paranthesis. It should to a degree support commas, by '.'
    (In other words: To use MATH, wrap a math-function in brackets, and it will attempt to resolve it).
    It additionally supports two custom, # and ¤. These extends what it was (If 2#2, it will do 2+2 (4). If -2#2, it will do -2-2 (-4). ¤ is the inverse, ie. 2¤2, would be 2-2 (0), -2¤2 would be -2+2 (0). You can do -2#-2, which is effectively the same as -2¤2).
    You may use ('sqrt' or '2rt' or 'q' for squareroot (^1/2)), ('2sin' or 'sin' or 's' for sinus), ('acos' or 'v' for Inverse cosinus), ('asin' or 'b' for Inverse sinus), ('atan' or 'n' for inverse tangent), ('tan' or 't' for tangent) ('2cos' or 'cos' or 'c' for cosinus), ('abs' or 'a' for absolute)
    Note, that you can chain several expressions in one (For instance {3*2+33/2} (should resolve to 22.5). Each of these could easily be variables. The variables can even contain the math-param, if you do it right (ie. don't end up with +- or *- etc))

    Math-related: You can get 'random' numbers, from passing two values into this:
    {Rand(0:10)}, would for instance output a number between 0 and 10.
    You could for instance get a number between 0 and onlineplayers by
    {Rand(0:{PlayersOnline})}.
    Note, that if min is greater than max, max will be set to min. (if 120 is passed as min, and 100 as max, max will become 120 aswell, but it will not be reflected upon the variables passed to it (No changes are made to any params passed to it))

    You can furthermore do math functions
    Ceil: {RoundUp(number)}, with number being a decimal.
    Floor: {RoundDown(number)}, <>
    Round: {Round(number)}, <>

    {pi} : Replaces with pi's first ten numbers.

    If you need to do a split operation:
    {Split([Param1][Param2][Param3])}
    The first [Parameter] should contain what you want split, for instance
    {Split([grains,fiber,protein,medkit])}
    The second [Parameter] should contain what you want to split it by, for instance, a comma",". You may specify multiple characters (For instance, if your list is [grains,fiber,protein,medkit]), you could specify ",."
    {Split([grains,fiber,protein,medkit])[,])}
    The third [Parameter] should contain a NUMBER, which index (0-indexed), you want returned.
    You can use 'first' and 'last' in addition to numbers. (First being the first entry, Last being the last entry)
    {Split([grains,fiber,protein,medkit][,][2])} : Would return "protein".
    {Split([grains,fiber,protein,medkit][,][0])} : Would return "grains".
    {Split([grains,fiber,protein,medkit][,][last])} : Would return "medkit".
    You must supply all 3 [parameters] for this to work properly.
    You may specify -1 in the third [], to get the LENGTH of the list, if it was to be split by the character. Note that contrary to NORMAL LENGTH function, this will be ZERO INDEXED (ie. for the list above, the returned number would be 3).
    (Zero-indexing: If you want the first item on the list, you need to write 0, if you want 4th item, you would write 3, etc.).

    {Length(<1>)} : Returns the amount of chars in a string (<1> being a string)
    For instance {Length(hello)} would return 5, as 'hello' got 5 chars.

    {CharAt([|><1><|][|><2><|])} : Returns the char at position <1> in string <2>
    For instance {CharAt([|>0<|][|>hello<|])} : Returns 'h', {CharAt([|>4<|][|>hello<|])} : Returns 'o', etc.
    (keep in mind, that this is 0 indexed! So if you are using {Length}, you need to minus 1!)

    If you need to do a replace operation:
    {Replace([ParamInput][ParamReplace][ParamReplaceWith])}
    The first parameter, ParamInput, will define what the replacement is being done ON.
    (for instance, 'icecream')
    The second parameter, ParamReplace, is what will be replaced.
    (for instance 'ice' would be part of 'icecream', and would be replaced)
    The third parameter, ParamReplaceWith, is what the ParamReplace will be replaced by.
    (For instance 'soft', would replace 'ice' in 'icecream' to 'softcream')
    {Replace([icecream][ice][soft])} : Replace 'ice' in 'icecream' with 'soft' : Output: 'softcream'

    If you need to find differences between two strings, you can use this method:
    {StringDifferences(<1>)[([<2>])][([<3>])]}
    <1> being:
    - items .. Will compare the <2> against the <3>, as if they were in the items format [()()()]
    - exact .. Will compare the strings against eachother, and output the differences, untill a similar match is found.. very specific.
    - language .. Will compare the strings somewhat intelligent, by phrases, and give differences. Note that dummy words such as 'a', etc is omitted i(C# Distinct())
    <2> being the baseline string
    <3> being the string that is expected changed.
    If <1> is items, will output in the same format
    Otherwise will output a CSV

    If you need to do a Contains operation (check if something is contained within a string)
    {Contains([toTest][lookfor])}
    The first parameter, toTes, being what is being inputted (the full string)
    (for instance, 'I like icecream. Its the best in the world!')
    The second parameter, lookfor, is what should be found in the string (above param).
    (for instance, 'icecream' or 'world' or 'cake')
    icecream would be a 'word' within the string, and return true
    world also is in the string, and returns true
    cake is not in the string, and would return false.

    If you need to do a IndexOf operation (check if something is contained within a string, and if so, where)
    {IndexOf([toTest][lookfor])}
    The first parameter, toTes, being what is being inputted (the full string)
    (for instance, 'I like icecream. Its the best in the world!')
    The second parameter, lookfor, is what should be found in the string (above param).
    (for instance, 'icecream' or 'world' or 'cake')
    icecream would be a 'word' within the string, and return 6, as that's the index it was found in.
    world also is in the string, and returns 36.
    cake is not in the string, and would return -1.

    You should make sure, that your split and replace operations are seperate from eachother, as they might not play well with eachother. Use with care atleast.
    You should also make sure, that any input to either of the two, have been resolved beforehand.

    You can use {Remove([<1>][<2>])} to remove x chars, from index.
    <1> is index (for instance 0, would be start of string)
    <2> is how many to remove from this index, for instance 2.
    With string 'This is a test string', and index: 0, remove: 2, it would remove 'Th'.
    With index: 5 and remove: 7, it would remove 'is a te'
    This operation is performed last, before returning the data.

    If you want to remove a data-entry in permadata entirely, you may use
    {DeletePerm([<1>])}
    <1> being the KEY, for instance 'originalY-0'
    Depending on wether it was removed or not, it will replace it with 'success' or 'failure'.
    If you are using a different file, for instance 'bounty', and have key 'status', please specify 'bounty:status' as <1>. If you just have 'status', it will remove key 'status' from ALL files having a reference to it.

    If you have a set, and you wish to interact with it, you can do the following natively:
    *setvariable: A reference to the set. Either from permadata (stored data) or references (temp data) or an input. If you have an entry in permadata with the key 'MasterSet', you would write that there.
    If you have a set, or you don't want to affect the primary set (in the add/remove case), you can instead of using a reference, getting it resolved by '{MasterSet}' or by providing the data.
    *setdelim: What the set is naturally split by. For instance, in a comma-seperated, this would just be a ','.
    *searchstring: What should be searched for. Supports * searching (wildcard)

    -Contains : Does the set contain a string, return true/false
    {setContains([<|setvariable|>][<|setdelim,|>][<|searchstring|>])}
    (If set at all contains the searchstring, it would return 'True')

    -Index : If set contains a certain string, return each occurence, CSV
    {setIndex([<|setvariable|>][<|setdelim,|>][<|searchstring|>])}
    (If first, and third contains the searchstring, it would return 0,2)

    -Add : Add something to the set
    {setAdd([<|setvariable|>][<|setdelim,|>][<|toadd|>])}

    -Remove: Remove every occurence of searchstring from the set
    {setRemove([<|setvariable|>][<|setdelim,|>][<|searchstring|>])}

    -Size : Gets the size of the supplied set
    {setSize([<|setvariable|>][<|setdelim,|>])}

    Example: If you have input
    {setContains([<|derr,e|>][<|,|>][<|*er*|>])} -- {setIndex([<|d,e,d,d|>][<|,|>][<|d|>])} -- {setAdd([<|d,e,d,d|>][<|,|>][<|d|>])} -- {setRemove([<|d,e,f,d,d|>][<|,|>][<|d|>])} -- {setSize([<|d,e,f,d,d|>][<|,|>])}
    It will output
    True -- 0,2,3 -- d,e,d,d,d -- e,f -- 5


    If you for whatever reason needs to iterate over something, either as a for (int i; i<i2;i++), or as a foreach("input","seperator") list, refer to 1.13.23. Keep in mind that this is done as the FIRST THING in an action (before 1.13.1).
    This section does nothing on it's own, but is highly usefull to set one or several variables, that you then use later on, based on conditions & iterations.

    If you want to break the forloop, you may add '{break}' to a ForLine (1.13.23.5.2.1), and it will stop the iterations.
    Keep in mind, that iterating over things MAY BE VERY RESSOURCE INTENSIVE! so you should do your best to break it as soon as possible, to minimize complaints.
    Also keep in mind, that the operations is exponentially more ressource demanding, the larger the text you are processing!

    You may in addition to the normal forloop, make use of Functions (1.26).
    These can be called dynamically like this:
    {Function([|><1><][|><2..><])}
    <1>: being a reference to the FunctionName (If FunctionName (1.26.1) is 'test', <1> would be 'test')
    <2..> : being optional! you can pass local variables, that is only accessible within the function.
    They can be accessed with '{local<num>}', with <num> being the position in the {Function([])}
    For instance, you can call
    {Function[|>test<]} : This is the basic, will invoke the 'test' function, and replace the tag with the 'returnval'.
    {Function[|>test<][|>a<]} : This passes one param, accessible with {local0} : Which would resolve to 'a'
    {Function[|>test<][|>a<][|>cake<]} : This passes two params, accessible with {local0} : Which would resolve to 'a', and {local1} : Which would resolve to 'cake'.
    You may pass any number of params.
    In the function, you may change the {local<num>} as you please, aslong as they were initially passed (in above example {local0} and {local1} would exist, while {local2} wouldn't)
    You may additionally change & call {returnval}. This is the final value, that the initial {Function(...)} will be replaced with.
    It will take the default value of DefaultReturn (1.26.2). If changed, it will end out as whatever it was changed to.
    You may at any point call {return}, to force termination, and return whatever the {returnval} was at that point.
    Otherwise the For (1.26.3) works as a normal For (1.13.23)

    If you need to get a TAG from a PLAYFIELD, you may do the following:
    Note, that if several of the tag is found, it will be displayed as a COMMA SEPERATED LIST!
    {PlayfieldTag([{Playfield}][Tag])}
    Where Playfield is a playfield, for instance 'gatih' (casing doesn't matter)
    And second is the tag you search for, for instance 'PvP' (casing DOES matter) (ie. 'pvp' when tag is 'PvP' wouldn't work).

    If you need to format text, you can get the first char capitalized, all to lower, or all to upper, by:
    - If you need to create lowercasing from an input, you may do
    {ToLower(input)}, where 'input' is what you want to lowercase
    input could for instance be 'cookie' or 'CooKie' or 'COOKIE', which would output 'cookie'.

    - If you need to capitalize the first letter of an input, you may do
    {CapFirst(input)}, where 'input' is what you want changed.
    input could for instance be 'cookie' or 'CooKie' or 'COOKIE', which would output 'Cookie', 'CooKie', 'COOKIE'.

    - If you need to capitalize every letter of an input, you may do
    {Cap(input)}, where 'input' is what you want capitalized
    input could for instance be 'cookie' or 'CooKie' or 'COOKIE', which would output 'COOKIE'

    Not documented in the main list, there is occasionally the ability to add a tag called 'Variable'
    This tag does nothing on it's own, but can for instance be used to set variables, like so:
    - Variable: "{ExecutedThis=0}"
    (Would set ExecutedThis to 0)
    These are intended as helpers, when you need to set variables, do math and save as variable, etc. They work like any other field, except they have no purpose on their own.
    (If you write anything here, you should pass it to a variable, as you otherwise have just done something for no reason)
    The Variables tag can for instance be used under 'Actions' and 'ExecuteCommand'

    In the actions section of an entry, you can specify a series of logic-conditions that must be filled, in order for the action-entry to be executed. If the logic all fails, it will not be executed.

    The structure of this section is:
    Actions: #You can chain these together, with the delay, and multiple entries, to make stuff like 'first announce that something is happening', 'then spawn something', etc.
    - Conditionals: #You are able to specify conditions, that must be met to 'do the action'. These are strings, where you will use either ==, >= or <=, >, <, !=. Note you can only use >< if it's a number. You can do A ><= B per statement. Each *Conditions* is an AND (All *Condition* must be met). If you have several *Conditions*, it's an OR (If any of them is met, it will fire)
    - Conditions: #OR
    - Condition: #AND

    If you have the 'Conditionals:' block, it will perform the logic.

    If *ANY* of the 'Conditions:' entries resolves to TRUE, it will allow the action to be fired. (OR)

    In order for a 'Conditions:' to be true:
    All it's 'Condition:' have to be true (AND)

    Example:

    Conditionals: #If any of these are true
    - Conditions: #Must not be resolved to something, and not have done the above.
    - Condition: "{target}==xbracketx"
    - Condition: "{didfirst}==false"
    - Conditions:
    - Condition: "{target}=={PlayerID}"
    - Condition: "{didfirst}==false"
    - Conditions:
    - Condition: "{target}=={PlayerName}"
    - Condition: "{didfirst}==false"

    Here we have THREE ways this action can be allowed to run:
    The target is NOT resolved to something AND The value of {didfirst} is 'false'
    Or The target is resolved to the executing players ID AND The value of {didfirst} is 'false'
    Or The target is resolved to the executing players name AND the value of {didfirst} is 'false'

    If any of these three statements fullfill BOTH their condition, the action will be allowed to run.
    Otherwise it will not.

    Related:
    The value of {target} is taken from the commandentry in this example, by
    - i: 1
    key: target
    type: entityid
    optional: true
    - i: 1
    key: target
    type: playerid
    optional: true
    - i: 1
    key: target
    type: playername+
    optional: true
    - i: 1
    key: target
    type: steamid
    optional: true
    - i: 1
    key: target
    type: clientid
    optional: true
    If any of the 'position: 1' resolves to anything, it will set the KEY "target", to that value.



    When using logic, you can do the overall AND, OR
    For each condition, you can do
    == (A is the same as B)
    != (A is different from B)
    => (A is greater than OR equal B)
    =< (A is less than OR equal B)
    > (A is greater than B)
    < (A is less than B)

    In the place of A and B, you can use any string.
    Either resolved, via the 'string-replacement' (using {key}'s),
    By stating a static value (for instance {key}==hello, would test the value for that key, against 'hello').
    By stating xbracketx, it will check if it was resolved. (have a value, or is the input {key})

    Note when using logic, that you should attempt to keep it lowercase for the most part.

    The logic runs on a custom logic-engine following these rules:
    - Supports ! where it will invert the following condition (for instance !(false) or !false would return true, !false&&true would return true, false||(!false) would return true, etc.)
    - Takes decimals as whole (8.0 == 8 would be true, 8.5 == 8 would be false)
    - Allows for multiple 8 == 8 >= 7
    - Supports spaces and . and , (for commas in numbers) -- Note however, that this means that (12 33,5 == 1233.5 is true)
    - Supports nesting with parenthesis (1==2||2==3||2==2)&&!(false||false)
    - text assumes true, if no conditionals attached ('sometext' is true, where 'sometext!=sometext' would be false)
    - Conditionals supported: != / =! / == / > / < / <= / =< / >= / =>
    - false and true is interpretated regardless of casing (True == true == tRuE etc)
    - Invalid entries is assumed false (test>=1 would be assumed false)
    - Supports blank entries
    - Support testing for brackets. If either right or left side of a conditional is 'xbracketx', it will evaluate, if the entry have brackets '{'. For instance {test}==xbracketx would return true, as it contains brackets, similar, test==xbracketx would return false, as it contains no brackets. Meanwhile {test}!=xbracketx would return false, as it contains brackets, while test!=xbracketx would return true, as it does not.



    Additionally to the logic section, you are able to use basic inline logic.
    The format is: {<>:<>:<>:<>:<>}
    <>1: first param
    <>2: second param
    <>3: What to compare first and second param with? (= or => or =< or < or > or !=)
    <>4: What happens if <1> logic<3> <2> is true
    <>5: What happens if <1> logic<3> <2> is false
    For instance
    {{PlayersOnline}:2:=:2 players online! woo:Aw. There is not exactly two players}
    If {PlayersOnline} resolves to 2 (2==2), it will tell you it was 2, otherwise that is wasnt
    You may additionally use | is <>1 and <>2
    For instance
    {{PlayersOnline}:1|2:=:There is one or two players:"Theres not one or two players exactly online"}
    (If you want to use : (colon) inline, you must encase it in '' (single-quotes) or "" (quotes))
    Keep in mind, that text comparesments is done with 'ToLower' (ie. everything is lowercase, so if you want to check for 'true', case it all lower).
    You can, to a degree, use this basic logic inline nested (ie. having logic inside logic). Just keep in mind, that it resolves INSIDE OUT (meaning that the most nested, is the first to be resolved). Keep in mind, that there is an upper limit to the amount of iterations allowed for a string. Usually it will run a maximum of 3 times.
    Please keep in mind, that it is possible to create an INFINITE LOOP, if you have two localization strings refer to eachother (via {Loca:key}). You should avoid this..


    [​IMG]
    If you want the ingame-responses to be named 'Perkyperk', and want to accomplish:
    Example 1:
    Code:
    CustomName: "[FF8C3C]PerkyPerk"
    Example 2:
    Code:
    CustomName: "[FF8C3C]PerkyPerk[-][FFFFFF]"

    This mod allows for relative-path use.
    This means, that you can, relative to where the mod is located (it's .dll file is placed), share a file, or several, with another mod.

    This allows you to have a central file, for your whitelist and/or specials list (VIP + reputation)

    Related config setting(s):
    SpecialsFile (Location of specials.yaml)
    SecFile (Location of security.yaml)

    You specify the paths relative to the current, by using ../

    For instance: if this mod is located at:
    D:\APPS\steamapps\common\Empyrion - Dedicated Server\Content\Mods\SpotGuard
    you would just type "security.yaml", if that file is located in the folder 'spotguard'.
    If the file was located in 'mods', you would type "../security.yaml"
    If the file was located in 'Content', you would type "../../security.yaml"
    If another mod, called 'PurchaseWithRep' exist, and you have the 'specials.yaml' file located in that mods folder instead, you could access it with "../PurchaseWithRep/specials.yaml"

    This mod allows you to run a server, that conveys messages to the mod.
    This means, that you can execute ingame happenings, from outside the game.

    You need to first enable the server, if you want it to run (see 12 AllowServer)
    You need to set the port you want to run the server on (see 13 ServerPort)
    (You should optionally portforward this port, so that you can interact from outside your network/device, if you so desire)
    You should set an API key, so that only people with this key are able to execute commands (you would usually place the key in a backend call from a website for instance). You should not expose this to others directly. (See 12 ServerAPIKey)

    Once you have configured it, it will run automatically, once you run this mod (ie. start your Empyrion server).

    In order to make a call to the API:
    Send a GET or POST request to
    http://<this_server_ip>:<port_from_config>/ModAPI/?<params>
    Where <this_server_ip> is the IP to the device running the server (for instance 127.0.0.1, if it's on the same device as you). <port_from_config> being config setting ServerPort, and <params> being what you want to accomplish from below.

    A param is
    a=b
    (for instance e=1014).
    To supply multiple params, use &
    (for instance q=command&e=1014&c=cake).
    Make sure that there's a ? infront of the params.
    (for instance http://localhost:8089/ModAPI/?q=command&e=1014&c=cake)

    So. What can you do with this API?

    You may pass data to it as such:
    'e' or 'entity': The entityid for the executing player (Who you want to run the command as)
    's' or 'steam': The steamid for the executing player (Who you want to run the command as). If supplied is picked over 'e' (if both are specified)
    'c' or 'command': What you are trying to accomplish ('Make the player type ingame', as if you were refering to a cmd from the config list. For instance 'transfer <otherplayer> IronOre 3')
    'z' or 'silent' or 'suppress': Would you want the player to get the error messages? Default: true (no messages. Pass false to give errors to player).
    'd' or 'date' or 'datetime': When you last asked for data (will give data since then, if the query allows).

    'q' or 'query': What you are querying for (Default: 'command').

    => q = 'Command':
    You want to execute a command as a currently ingame player.
    Required params: ('e' or 's') and 'c'. Supply entityid or steamid, and the command you wish to execute.
    'e': Entityid
    's': steamid
    'c': command to execute
    Example:
    http://localhost:8089/ModAPI/?q=com...=1044&c="transfer exacute ironore 10"&z=false
    q = 'OnlineList':
    Returns a list of all players online, including steamid, entityid and name. Note that the supplied name is always all-lowercase.
    Example:
    http://localhost:8089/ModAPI/?q=online&a=somerandomstring
    q = 'ActionsSince':
    Returns a list of actions happened since a date. Will cap the results to 100 newest. If date supplied, ('d') will only result 'newer than'.
    Optional param: 'd'
    'd': DateTime
    Example:
    http://localhost:8089/ModAPI/?q=actionssince&a=somerandomstring&d=5/18/2018 3:37:3
    q = 'Reload':
    Reloads the config file.
    Example:
    http://localhost:8089/ModAPI/?q=reload&a=somerandomstring
    q = 'Charges':
    Adds or sets charges for a specific user. Note that the steamid IS NOT VERIFIED (entry is created blindly for this entry, if it didn't have one)
    Required params: 's', 'e' and 'c'. NOTE THAT THEY ARE USED DIFFERENTLY HERE!
    's': steamid
    'e': +number, or -number, or number (ie +10 or 10 or -10). Adds, subtracts or sets.
    'c': Commandreference
    Example:
    http://localhost:8089/ModAPI/q=charges&a=somerandomstring&s=123&e=+100&c=cakeisnotalie
    (Would ADD 100 charges to player with steamid 123, for commandkeyentry 'cakeisnotalie')

    If you wish to use the API to make an EXTERNAL call (ie. the above is *this mod* receiving), you may use the ExternalCall part. (1.13.7.23). These can be used to create GET's, by not using any data, or a POST, if you specify data.

    If you are receiving data, it will be in a 'strange format', that is explained below.
    This is so that you can actually work with the data, in the tool.
    You are most likely going to want to use 'advanced' functions such as 'for loops', indexof, splits, replace, and the like.

    The data comes in the following format:
    Key|0:|value
    (For instance 'Playfield' and 'Tallador' => "Playfield|0:|Tallador" )
    If there are multiple keys on the same tier, they will be seperated by |0,|
    (For instance 'Entities' and 'SomeListOfEntries' => "Playfield|0:|Tallador|0,|Entities|0:|SomeListOfEntries" )

    This is in the case, where we only have the primary layer. In the above example however, 'SomeListOfEntries' would be an actual list.
    Lists are seperated by their 'nesting level', for instance |1-|

    SomeListOfEntries would look something like
    "id|1:|1002|1,|pos|1:|SomeOtherEntries|1,|type|1:|25|1-|id|1:|1003|1,|pos|1:|SomeOtherEntries|1,|type|1:|8"

    Here, you would be able to first split it by |1-|, to get each entry on the list
    id|1:|1002|1,|pos|1:|SomeOtherEntries|1,|type|1:|25
    id|1:|1003|1,|pos|1:|SomeOtherEntries|1,|type|1:|8
    And then it is effectively the same format as the first tier.

    It would now look something like this
    "Playfield|0:|Tallador|0,|Entities|0:|id|1:|1002|1,|pos|1:|SomeOtherEntries|1,|type|1:|25|1-|id|1:|1003|1,|pos|1:|SomeOtherEntries|1,|type|1:|8"

    However, like before, this isn't the end. There's another list of entries, 'SomeOtherEntries'

    It follows the exact same pattern as before, but it is not a list, so it doesn't have seperators.
    This could be
    "x|2:|14248|2,|y|2:|163.5|2,|z|2:|3960"
    Notice, how it basically follows the same pattern as the first layer, but just with 2, rather than 0, as we are '2 levels down'.

    This would make our string look something like
    "Playfield|0:|Tallador|0,|Entities|0:|id|1:|1002|1,|pos|1:|x|2:|14248|2,|y|2:|163.5|2,|z|2:|3960|1,|type|1:|25|1-|id|1:|1003|1,|pos|1:|x|2:|14136|2,|y|2:|169|2,|z|2:|3816|1,|type|1:|8"

    Now you know how it is built. To get a specific entry in it, you should approach it in the same manner, from tier 0, and forward.

    So you start at 0, and perhabs split it by the ',' first (|0,|)
    Then you might look at the values, and decide it is the one that contains 'Entities' you are interested in.
    Then you might split it by the ':' (|0:|), and keep the remaining data.
    This would leave you with something like
    "id|1:|1002|1,|pos|1:|x|2:|14248|2,|y|2:|163.5|2,|z|2:|3960|1,|type|1:|25|1-|id|1:|1003|1,|pos|1:|x|2:|14136|2,|y|2:|169|2,|z|2:|3816|1,|type|1:|8"

    Now we have effectively eliminated the first tier, and are now at tier1.
    This tier is where we make an actual decission as to what data we are interested in. In this case the ID.
    First, we iterate over them, by splitting from their tier (1 => |1-|)
    This leaves us two entries in this case:
    id|1:|1002|1,|pos|1:|x|2:|14248|2,|y|2:|163.5|2,|z|2:|3960|1,|type|1:|25
    id|1:|1003|1,|pos|1:|x|2:|14136|2,|y|2:|169|2,|z|2:|3816|1,|type|1:|8

    For each of these entries, we split it up by the ',' (|1,|)
    id|1:|1002
    pos|1:|x|2:|14248|2,|y|2:|163.5|2,|z|2:|3960
    type|1:|25

    Then we can either look if it contains the keyphrase 'id', or split it further by the colon. In this example we determine it's the first line we are interested in, as it contained 'id'.
    Then we split by the ':' (|1:|), leaving us with the number
    1002

    We do the same for the next entry, as we iterate over them, and now have the numbers
    1002 and 1003
    We can now compare that, to whatever number we were looking for, let's say 1002.
    Since we only cared about that entry, we 'return' to it (As we are still practically looping, it's not really returning, but yeah)
    Now we have one line remaining.

    id|1:|1002|1,|pos|1:|x|2:|14248|2,|y|2:|163.5|2,|z|2:|3960|1,|type|1:|25

    We can split by the ',' (|1,|) first, to begin extracting the relevant data.
    id|1:|1002
    pos|1:|x|2:|14248|2,|y|2:|163.5|2,|z|2:|3960
    type|1:|25
    Then we could simply index it, or know from what we are looking for, that it is the 2nd and 3rd, that we are interested in, as we already have 'used' the first, in this example.
    We split by ':' (|1:|), and keep the second part only, leaving us with
    x|2:|14248|2,|y|2:|163.5|2,|z|2:|3960
    25
    We can store the type and use it for whatever we wanted, but to make use of x y z, there's some additional steps.

    As initially, we can split by their ',' (|2,|), giving us
    x|2:|14248
    y|2:|163.5
    z|2:|3960
    Now, we can again 'cheat' and know which is in which order, or we can do indexof, and determine it by that.
    Then to finally extract the value, we split by ':' again (|2:|), keeping the second part only
    14248
    163.5
    3960
    And now we can store those for x, y and z, and use that for what we wanted.

    ----

    This is a 'brief example' of how it works. Basically the format is
    key:value,key:value
    But to effectively use it, it got it's tier number before the ':' and ','. Furthermore it's encased in '||', to prevent it from colliding with actual data.

    In the case where we might have a list of multiple entries, it is 'seperated' by it's tier '||'

    ----
    Finally, here's the blob of data, that the above example was extracted from. It is the return for an actual call, incase you want to see it in context.
    playfield|0:|Tallodar|0,|entities|0:|id|1:|1002|1,|pos|1:|x|2:|14248|2,|y|2:|163.5|2,|z|2:|3960|1,|type|1:|25|1-|id|1:|1003|1,|pos|1:|x|2:|14136|2,|y|2:|169|2,|z|2:|3816|1,|type|1:|25|1-|id|1:|1004|1,|pos|1:|x|2:|-3416|2,|y|2:|62.5|2,|z|2:|7976|1,|type|1:|25|1-|id|1:|1005|1,|pos|1:|x|2:|8328|2,|y|2:|57.5|2,|z|2:|1368|1,|type|1:|25|1-|id|1:|1006|1,|pos|1:|x|2:|-2856|2,|y|2:|94.5|2,|z|2:|7176|1,|type|1:|25|1-|id|1:|1007|1,|pos|1:|x|2:|-2984|2,|y|2:|88.5|2,|z|2:|6776|1,|type|1:|25|1-|id|1:|1008|1,|pos|1:|x|2:|-10536|2,|y|2:|66.5|2,|z|2:|-3688|1,|type|1:|25|1-|id|1:|1009|1,|pos|1:|x|2:|-536|2,|y|2:|46.5|2,|z|2:|-2632|1,|type|1:|25|1-|id|1:|1010|1,|pos|1:|x|2:|-10104|2,|y|2:|51.5|2,|z|2:|-1848|1,|type|1:|25|1-|id|1:|1011|1,|pos|1:|x|2:|-4520|2,|y|2:|38.5|2,|z|2:|-4504|1,|type|1:|25|1-|id|1:|1012|1,|pos|1:|x|2:|-15512|2,|y|2:|67|2,|z|2:|-2152|1,|type|1:|25|1-|id|1:|1013|1,|pos|1:|x|2:|-12184|2,|y|2:|63.5|2,|z|2:|-1528|1,|type|1:|25|1-|id|1:|1014|1,|pos|1:|x|2:|-11720|2,|y|2:|56.5|2,|z|2:|4360|1,|type|1:|25|1-|id|1:|1015|1,|pos|1:|x|2:|14472|2,|y|2:|37.5|2,|z|2:|7976|1,|type|1:|25|1-|id|1:|1016|1,|pos|1:|x|2:|376|2,|y|2:|68.5|2,|z|2:|7768|1,|type|1:|25|1-|id|1:|1017|1,|pos|1:|x|2:|1016|2,|y|2:|83.5|2,|z|2:|3304|1,|type|1:|25|1-|id|1:|1018|1,|pos|1:|x|2:|4440|2,|y|2:|67.5|2,|z|2:|5368|1,|type|1:|25|1-|id|1:|1019|1,|pos|1:|x|2:|-11720|2,|y|2:|78|2,|z|2:|3400|1,|type|1:|25|1-|id|1:|1020|1,|pos|1:|x|2:|-11800|2,|y|2:|58|2,|z|2:|6552|1,|type|1:|25|1-|id|1:|1021|1,|pos|1:|x|2:|6376|2,|y|2:|45|2,|z|2:|-6888|1,|type|1:|25|1-|id|1:|1022|1,|pos|1:|x|2:|-72|2,|y|2:|38|2,|z|2:|5208|1,|type|1:|25|1-|id|1:|1023|1,|pos|1:|x|2:|-1256|2,|y|2:|52|2,|z|2:|3608|1,|type|1:|25|1-|id|1:|1024|1,|pos|1:|x|2:|-10504|2,|y|2:|43|2,|z|2:|-5832|1,|type|1:|25|1-|id|1:|1025|1,|pos|1:|x|2:|8|2,|y|2:|159|2,|z|2:|3288|1,|type|1:|25|1-|id|1:|1026|1,|pos|1:|x|2:|-360|2,|y|2:|22.5|2,|z|2:|-4008|1,|type|1:|25|1-|id|1:|1027|1,|pos|1:|x|2:|-7560|2,|y|2:|44.5|2,|z|2:|-472|1,|type|1:|25|1-|id|1:|1028|1,|pos|1:|x|2:|-5592|2,|y|2:|58.5|2,|z|2:|-2632|1,|type|1:|25|1-|id|1:|1029|1,|pos|1:|x|2:|-4280|2,|y|2:|39.5|2,|z|2:|-1784|1,|type|1:|25|1-|id|1:|1030|1,|pos|1:|x|2:|-5640|2,|y|2:|43.5|2,|z|2:|-648|1,|type|1:|25|1-|id|1:|1031|1,|pos|1:|x|2:|5576|2,|y|2:|43.5|2,|z|2:|1752|1,|type|1:|25|1-|id|1:|1032|1,|pos|1:|x|2:|-6616|2,|y|2:|44.5|2,|z|2:|-936|1,|type|1:|25|1-|id|1:|1033|1,|pos|1:|x|2:|-15976|2,|y|2:|43|2,|z|2:|3112|1,|type|1:|25|1-|id|1:|1034|1,|pos|1:|x|2:|-1624|2,|y|2:|50|2,|z|2:|4600|1,|type|1:|25|1-|id|1:|1035|1,|pos|1:|x|2:|14984|2,|y|2:|96|2,|z|2:|-4776|1,|type|1:|25|1-|id|1:|1036|1,|pos|1:|x|2:|-15928|2,|y|2:|68|2,|z|2:|744|1,|type|1:|25|1-|id|1:|1037|1,|pos|1:|x|2:|-10712|2,|y|2:|25.5|2,|z|2:|-744|1,|type|1:|25|1-|id|1:|1038|1,|pos|1:|x|2:|15992|2,|y|2:|40|2,|z|2:|4056|1,|type|1:|25|1-|id|1:|1039|1,|pos|1:|x|2:|-15304|2,|y|2:|36|2,|z|2:|-3864|1,|type|1:|25|1-|id|1:|1040|1,|pos|1:|x|2:|-2456|2,|y|2:|38.5|2,|z|2:|920|1,|type|1:|25|1-|id|1:|1041|1,|pos|1:|x|2:|0|2,|y|2:|140.1965|2,|z|2:|0|1,|type|1:|24|1-|id|1:|1049|1,|pos|1:|x|2:|-4277.846|2,|y|2:|50.24514|2,|z|2:|428.0253|1,|type|1:|8

    'Valuta' is a term covering over
    -Having an entry with a corresponding value
    How you use the entry is up to you. It is possible to use only by replacecmds, by using as a requirement, or by using as a cost.

    Valuta consists of two files:
    'valuta.yaml' and 'valutahelper.yaml'

    The valuta.yaml file contains the actual information, for any valuta, for any player, while the 'valutahelper.yaml', is where you can 'outline' the valutas initially.

    You can, with the valutahelper, specify:
    - Description: The text associated with the entry
    - Typeof: Wether it should be counted as 'Currency' or 'Reputation', for the 'overview-screens'
    - Initial: What it should initially start out as
    It is based on : 'Origin' (Their origin, int) > 'OriginPlayfield' (Their start playfield) > default (any with '-1' as value). If none were found, it will start as '0'.
    - Intervals: Can be used to reference it later, as a value. (For instance 'Friendly' can be from 10, and 'Honored' from 50), so when referenced, it will compare to '10' for 'friendly'.
    - Category: What category to sort it under. Default is 'unsorted'. (Will group the returned entries by this)
    - MaxValue: if specified, will prevent any value being entered/accumulated over this.

    You need to match 'Name' with what you use when using it (for instance 'TestFaction', for it to apply).

    Code:
    Entries:
    - Name: TestFaction #Name, when using the faction
      Typeof: reputation #What should the type be? ('currency' or 'reputation'. Depends on which entry it shows up)
      Intervals: #When referenced, or compared, what should be returned? Note it's from <interval start> (So if value is 10, it will be 'friendly', if value is 50, it will be 'honored', etc. Custom names)
      - Friendly: 9
      - Hated: 0
      - Honored: 50
      Description: Testfaction is a test reputation. #What should be used to describe this?
      Initial: #What should the valuta start out as?
      - Origin: -1 #If nothing matches : use value '10'
        Value: 10
      - Origin: 1 #If origin matches '1' : use value 50
        Value: 50
      - OriginPlayfield: 'Tallador' #If starting playfield matches 'Tallador' : use value 70
        Value: 70
    
    You can use valuta in Requirements and in Cost.
    You can dynamically interact with them by 'ValutaGet','ValutaAdd','ValutaSet','ValutaResolve'. See corresponding replacements.

    You can use these as you please, but the intention is to either use them as currency, or as reputation.
    An example usuage could be hooking it up to a 'CompletedChapter' event, and granting some reputation based on that, or whatnot. Really, it's up to your creativity.

    Note, that an entry *does not* have to exist in 'valutahelper' to be used. Just picking a name is enough.

    An entry in the 'valuta' does not have to exist either, to use it. It will be dynamically created, as players interact with your commands.

    Example on valuta.yaml
    Code:
    Entries:
    - name: TestValuta
      steamid: 76561197987322437
      LastChanged: 2018-08-07T20:35:28.6770904+02:00
      value: 26
    - name: TestFaction
      steamid: 76561197987322437
      LastChanged: 2018-08-08T16:10:39.8454358+02:00
      value: 50
    
    Steamid : Who it belongs to
    name : What name was used
    LastChanged : Autoupdates to when last it was changed
    value: The value for this valuta.

    If you wish to update the file, make sure to reload config file asap, or do it before the server is running.

    You may edit it as a player ingame by command
    Syntax: '.cc vl <steamid> <valutaname> <value>' : Valutaname will be case insensitive, if an entry exist in 'valutahelper' (ie. if input is 'testfaction', and 'valutahelper' have an entry on 'TestFaction', it will autocorrect to this. Otherwise it will use the input). If value have a '+' infront, it will add to what the user have. Otherwise it will set it to this value. Note that the command, if 'add', will respect any default for the valuta, and add ontop of this.

    SpotGuard delivers a lot of functionality to the mod, such as players onlinetime, their reputation, and if they are VIP.

    This means, that you must have a functional version of SpotGuard running, to make use of CC.
    However, it does not mean, that you have to actively use SpotGuard.
    You can effectively disable everything that SpotGuard does, and still have it deliver the data required for this mod.
    The easiest thing to do, would be to just set the spots, in SpotGuards config file, to something higher than your server can handle, so it doesn't ever reach it, and disable AFK kicking.

    The important thing is the 'specialslist.yaml', and its content.
    Please do not just create one yourself, as it will not work, if players are missing from it.

    Additionally SpotGuard provides convenient commands to manage the file, such as increase/set their reputation, their VIP, Language setting, etc.
     
    #5
    Last edited: May 15, 2019
    Ephoie likes this.
  6. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    FAQ
    I've had people raising concern on the fact, that they cannot verify what the mod does themself, as it is closed-source.
    That's a fair concern, but one that I would like to drop a few words on:

    First of all, it's a question of trust. If you do not trust, that I have your best intention, then that's that. Nothing I can do about it.

    However, you should keep in mind, that I'm an European citizen, and hence bound by the GDPR.
    And since, no GDPR notice is given, or accepted, it is fair to assume, that no data is transmitted elsewhere.

    To clarify: This mod _does not_ send any data to a third party, unless you specifically ask for this to be the case.
    There is a number of methods, where you can exchange data:
    -If you have the API enabled, it will always serve some information, such as online users, when called. Note however, that the API is disabled by default, and furthermore 'protected' by needing a token to access the data (token set by you in the config file).
    -If you are using 'ExternalCall' for any commands, the data specified here, will be sent to the location specified in it. You can exchange pretty much any data in this manner, but is it clear, from reading any .yaml, if the script does this or not.
    Please keep in mind, that 'ExternalCall's work, even with the API disabled, and should not require any additional portforwarding, as it is sent over http.
    -This mod answers request to the API internally aswell, so if another mod, such as SpotGuard asks for it, with the proper key, it will serve it.
    Keep in mind, that if you have no reason to expose the API to the 'outside', not to portforward whatever port you pick for the API. In this case, it will only be usuable internally.

    (Note, 'specifically ask' means that you do not make use of any .yaml files, that does this, or that you use the API to do so. You should always verify the custom scripts, but that much should be a given.)

    Another concern is on the mod crawling your files, making changes, etc.
    This is a thing, that the mod does.
    However, unless you have configured it otherwise, it will never leave the 'dedicated server directory'.
    Data crawled is data from SpotGuard, itself, basic data on playfields, the seed, configs, and similar.
    There is some commands that can interact with other files, read/delete/change them, but they are _exposed in .yaml files_ if used.

    As I initially touched on, this comes down to trust.
    You can however verify my claims above, by monitoring your network traffic with a tool such as Wireshark or similar.
    You can likely also verify which files are touched, but I'm not aware of any software to easily do this.

    Note that this shouldn't be taken as a legal statement, but rather an attempt to clarify any confusion.
    There might be other commands, than mentioned above, that can accomplish this, but the above should be mostly accurate atleast.

    Special thanks
    Contributors (files shipped with mods):
    VirtualBackpack - Taelyn

    Translations:
    Danish - Androggles
    Dutch - Taelyn
     
    #6
    Last edited: Aug 30, 2018
    Ephoie likes this.
  7. Ephoie

    Ephoie Captain

    Joined:
    Jan 27, 2018
    Messages:
    329
    Likes Received:
    515
    Nice piece of work here! Awesome Sauce @Exacute
     
    #7
    Exacute likes this.
  8. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    v1.01 (Important!)
    *Fixed issue where, when using a target, it would randomly overwrite as the executing player aswell.
    *Fixed API to actually be port-forwardable, rather than only locally. (Also both IP+localhost+127.0.0.1 should work locally)
    *Playerlocation now shipped with default utility - Allows for lookup on a player, and an admin tp to a player. (Thanks to @Taelyn for these)
     
    #8
    Ephoie likes this.
  9. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    v1.1 Released. Please make sure to update to SpotGuard v1.33!

    This update have a ton of bugfixes with it, but the main focus have been around the newly implemented concept 'valuta'.
    A is basically a number, associated with an unit. Very free, in how you can use it. The most obvious applications is to use it as 'Reputation' or 'Valuta', but you could prob. also implement custom stats, and whatnot. It's up to you how/if you use it.
    The mod natively supports it in Requests and Cost, and have 'string-replacements' to interact with it.
    All valuta is kept in 'valuta.yaml' and 'valutahelper.yaml'.
    You can for instance, if used as reputation, set intervals, that can be resolved, such as 'requires honored with <factionname>' (factionname would be valutaname in this case).
    So despite it being named 'valuta', it's really just a tool for you to do crazy stuff.
    I'm planning on making active use of it for some demo-commands later on, but I felt it was best to get this update out, rather than holding it for longer :)

    Among the notable fixes is the MATH engine have gotten an overhaul, to now support a little more complex math, aswell as being more consistent and flexible.

    Another thing worth noting is that the mod now supports two languages: Danish and Dutch, thanks to Androggles and Taelyn respectively.

    Last 'big mention' is for the for-loop system. These have gotten a lot of love, and is now noticeable faster. Although still not ideal. This area will receive more love in a later patch.
    The for-loops can also now be used as primitive functions, so you can call them inline. (see documentation)

    Taelyn have also submitted a 'virtualbackpack' implementation, using the mod, which allows players to have two virtual backpacks, that can be accessed any time. This is a very simple version, and can easily be expanded upon, if you so desire.

    Please note, if you are using the 'ExternalCall's.
    The parameter previously known as 'url' have been renamed to 'CCurl', to not conflict with actual parameters requiring an URL ;)
    (An exciting little bit here, is that you can also take RETURNS using this function, so you can for instance read a file on a server). No fancy support yet, as the game doesn't have any native support for JSON. Simple text-returns only.

    A bunch of assorted changes past these, few new hooks and other QoL features and such.

    v1.1
    *Added new 'AdminRank' to config file. This can be used to limit who can use admin commands, by their admin-level.
    *Added loca string for Main_Error_AdminDenied
    *Fixed ingame-message for minutes left, if command on cooldown, being limited to max 60, where it can be more. (visual)
    *Edited 'url' in 'ExternalCall' to 'CCurl', to not conflict with services, that might take POST of 'url'. IF YOU ARE USING 'ExternalCall' please EDIT THE 'url' param to 'CCurl'
    *Added 'CCreturn' to 'ExternalCall'. This will allow for commands to be executed, with the returned data from a call, as a param.
    *Mildly improved replace mechanism in regard to player, target and faction strings. (Small overall performance)
    *Added new 'Contains', {Contains(<>)}, that will return true or false, if it was contained in the string. Previously how 'IndexOf' worked.
    *Changed behaviour of 'IndexOf' to return a number. -1 if not in the string, otherwise the index of which the phrase searched for, was in the string. (As expected with 'IndexOf')
    *Updated demo-commands making use of these.
    *If a player is not member of a faction, {PlayerFactionResolved}, {TargetFactionResolved} will now resolve to 'No Faction' and {PlayerFactionResolvedAbbrev}, {TargetFactionResolvedAbbrev} will resolve to 'None'
    *Added updater for 'all structures' and 'All factions', to run every 30 minutes. (Updates all known-factions and known-structures)
    *Players factions will now be updated, when they change it. (Data-wise for this mod)
    *New hook for players changing factions, 'FactionChange', which can tell old and new factionID
    *Added native 'min' and 'max' to the type:int. And new localization entries, Main_Error_Expected_Min and Main_Error_Expected_Max.
    *New hook for players buying an item from a trader, 'ItemSold'.
    *Added new replace, {Remove([index][amount])}.
    *Added new replace, setAdd, setRemove, setContains, setIndex. Please see documentation. These are for working with sets (something split by something else, for instance a CSV)
    *Added new replace, {DeletePerm([<1>])}. This can be used to *delete* permanent data.
    *Added new replace, {ChargesLeft} and {AmountUsed} to get stats on usuage of this command (from executing players POV)
    *Added new config entry, 'ValutaFile'. Default 'valuta.yaml'
    *Added new file 'valuta.yaml'
    *Added new file to backups
    *Added new replace, ValutaGet, ValutaSet, ValutaAdd. Please see documentation.
    *Added new langstrings, Main_Error_NotExactCustom, Main_Error_TooMuchCustom, Main_Error_TooLittleCustom.
    *Added valuta as condition for Requirements. Please see 1.8.13.
    *Added modulus to math: You can now use '%' aswell.
    *Added replace, FactionsOnPlayfield, to get all IDs of factions inhibiting a playfield.
    *Tweaked replace, FactionMembers, to accept '0', and return all factionless in this case.
    *Fixed issue with 'NestToEntry' in 'forloops' lowering text, and hence not properly doing replacements.
    *Fixed issue with math function outputting non-numbers (such as xxxE-8). Should now always output decimals.
    *Added 'powerof' to math. (A^B) '^'
    *Fixed multiple issue with maths' sqrt not working.
    *Fixed issue with paranthesis not qualifying for math-regex.
    *Added 'sin' and 'cos' to math. (sin(10) cos(10) for instance)
    *Added 'pi' as a math keyword. (π)
    *Added replace for {pi} to resolve to pi's first 10.
    *Added new replace, setSize, to get the size of a given set.
    *Added default values to help command.
    *Added MoveEntity, to move entities around (player + structure + ..)
    *Added new 'utility', 'scatterplayers'. Allows to scatter players on a playfield
    *changed replace, rand, to now accept negative numbers aswell.
    *Fixed issue with math where it would not properly handling negative inputs (-1 for instance), if start of thread.
    *Fixed issue with math where it would not handle +-, --, -+. (These now respect proper math rules)
    *Fixed issue with some types not using 'defaultvalue', such as 'faction' and 'playfield'.
    *Changed type:factions to use their ID for reference, rather than the input.
    *Added support for custom 'dedicated.yaml' versions (ie. that file with other filename(s)), in loading seed + savegame (used for some tag-searching). This will happen automatically, if one such file is found, will take precedence over dedicated.yaml's content.
    *Added new 'utility', 'scatterplayers > tpa'. Allows to teleport all players to the player, or to a playfield. Can additionally be only all members of a faction (Note, A8.2: Please do not use it to teleport to another playfield, it will break your player, and will put you on another playfield on relog (most likely))
    *Added new replace, EntityInfo, allowing to get information on structures & players. See docu.
    *CC now creates a 'ghostplayer' untill SG have detected the player (incase CC discovers it before SG). This should help with 'player not found' errors on first-join, and should allow for successfull execution of the related commands. Note, that rep might function a little weird in this case.
    *Fixed minor issue if players inventory doesn't consist of anything on first-join
    *Fixed minor issue with conditionals
    *Changed all 'Item' related to allow for no items (meaning you can now use the POI spawn without attachin items to core destruction, and open a blank itemwindow, accepting items)
    *Added new 'utility', 'thrashcan', allowing the player to easily delete multiple items by shift clicking into a blank window.
    *Added new requirement, 'PlayerName'. Can be used to black/whitelist specific players from/to a command.
    *Added new lang strings, Main_Error_BlacklistedName, Main_Error_NotWhitelistName, Main_Help_NameBlacklisted, Main_Help_NameNotWhitelisted
    *Added 'PlayerName' to help entries.
    *Fixed potential dupebug, for commands using cost-references, where one item could substitute another.
    *Added new lang strings, Main_Help_TooLittleCustom, Main_Help_NotExactCustom, Main_Help_TooMuchCustom.
    *Added valuta to help entries.
    *Added new lang string, Main_Error_NotEnoughCustom
    *Added valuta to cost
    *Fixed issue where cost would occasionally be ignored (Allowed to use, despite not having)
    *Added valuta pay to help entries.
    *Added to the I/O format of inventory, decay and ammo, as optional params.
    *Updated relevant replace, to handle 3-5 (), to make use of above.
    *Updated return of items, to make use of above.
    *Updated stringdifferences for item, to make use of above.
    *Added 'ammo' and 'decay' to item, when used to send
    *Added new file 'valutahelper.yaml' - for additional valuta data
    *Added new config key, 'ValutaHelperFile' (Default 'valutahelper.yaml')
    *Added new replace, 'ValutaResolve' (see docu). Interacts with the 'valutahelper' file, to give extended info on a valuta.
    *Added support for using 'Intervals' in cost > ValutaReference. (such as 'friendly'. Please make sure that the entry exist, as it will otherwise compare with value '0'). Keep in mind, that this is not exactly recommended, as it converts it to the value. For instance 'honored' would in 'TestFaction' example convert to 50, and hence cost player *50* every time. Make sure you intend to use it as such, if you do.
    *Added Initial to 'valutahelper.yaml': this can be used to set a default (what the valuta should start out as). It will be determined, first time an user needs the valuta.
    It is based on : 'Origin' (Their origin, int) > 'OriginPlayfield' (Their start playfield) > default (any with '-1' as value). If none were found, it will start as '0'.
    *Added possibility to have {} in valutareferences aswell (will be resolved).
    *Added support for using 'Intervals' in requirements > ValutaReference.
    *Added support for using 'Intervals' in help commands.
    *Added lang string Main_Error_ValutaWrongFormat, Main_Error_MissingArgument, Main_Success_AddedValuta, Main_Success_SetValuta
    *Added new command to set valuta. Syntax: '.cc vl <steamid> <valutaname> <value>' : Valutaname will be case insensitive, if an entry exist in 'valutahelper' (ie. if input is 'testfaction', and 'valutahelper' have an entry on 'TestFaction', it will autocorrect to this. Otherwise it will use the input). If value have a '+' infront, it will add to what the user have. Otherwise it will set it to this value. Note that the command, if 'add', will respect any default for the valuta, and add ontop of this.
    *Added new replace, ValutaList. See docu.
    *Added new replace, Function. See docu.
    *Added new 'Functions' Will allow for better and more dynamic use of previous for-loops (These still exist)
    *Now allowing 4 additional layers of command-nesting for for-loops (if you are using them, it is expected you know what you are doing, so the added complexity is to allow for more advanced use)
    *Fixed issue when using multiline combined with colons, in ChatMessages (Text, TextAll (..))
    *Added new replace, 'Length', giving the amount of chars a string contains (hello = 5, etc)
    *Added new replace, 'CharAt', giving the char at some position (0-indexed).
    *Added new 'default', 'valutaoverview.yaml', containing 'ReputationView' - Showing all valuta in 'typeof' reputation. Also containing 'CurrencyView' - Using the same approach, but intended for currencies.
    *Added tag 'Creator'. Can be used to 'sign your work', under an entry. It doesn't actually do anything, but is, obviously, visible in the file.
    *Added tag 'DefaultLanguage' to config, default 'English'.
    *Updated internal values to SpotGuard v1.33
    *Added listener for 'lang' in chatmessages, will update users lang. It will read the update after 10s.
    *If user exist on SpotGuards specialslist, and have a 'language' entry, it will use this as the local language. (Try to localize based on this).
    *When running a command, will always check for changed language by the user, based on SGs Specialslist.
    *Added debug timing to for-loops to give a better idea of how expensive they are (And to better help optimise them)
    *Tweaked behaviour of {break} in for-loops, to only break this iteration, rather than the entire thing. Use {return} to stop it entirely (and return the value), as would be expected behaviour of normal programming.
    *Added Que under lastused. Will make use of a dummy entry, 'System_Pending_Commands', one for each steamid. If they have anything qued, will be under 'Qued'
    *Added new replace, QueCommand, to que/execute a command for a steamid (if they are not online, command will be qued for next they are online)
    *Added ability to do {variable+=value}, to add it to the current. Note! this is a string addition, NOT numerical. (Meaning if you do +=2, and had 2 already, it would become 22 not 4).
    *Optimized for-loops, and especially functions a lot.
    *Added prefix for localized variables inside functions/for-loops: If using 'local_', they will only exist inside this scope (and be more efficient)
    *Now allowing 'defaultvalue's to be blank (""). Note that their default is still "NA" (ie. won't resolve to anything, unless specified)
    *Fixed issue with using the tag 'Special' under 'Item' under 'Action'. If passed "" (blank), it wouldn't deliver a blank stack, but rather return an error.
    *Updated helper function for inspectplayer, to no longer edit the 'executing players' inventory.
    *Added 'Extension' By Taelyn, VirtualBackpack. Script allowing to access two virtual backpacks for each player, by default.
    *Added translation for 'Dutch', thanks to Taelyn!
    *Added translation for 'Danish', thanks to Androggles!
     
    #9
    Ephoie and Taelyn like this.
  10. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    Notice hotfix:
    v1.11
    *Fixed minor issue where 'Version' was 'version' in yaml file.
    *Fixed issue with users not being properly un-registered, due to some new parameters.
    *Wrong localization file was included in v1.1. Added proper one.

    Notice: Unknown issue currently with valuta. I will look into it when possible, but please refrain from trying to actively using it right now.
     
    #10
    Ephoie and Taelyn like this.
  11. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    This is a smaller patch, but I was made aware of an issue, where the mod would start hugging CPU. This is mainly directed at fixing this.
    If you still experience this issue, please do let me know.

    v1.12
    *When using 'ItemCustomName', it will now support string replacements (For instance using {PlayerName})
    *Fixed an issue with a watcher not behaving properly, and spawning childs unintentionally.
    *Fixed minor conflict with some events conflicting on teleport, and watchdog. Not really any impact, but a little neatness cleanup.
    *Fixed issue where the cost (credits) would not be subtracted from the player on execution.
    *Changed default config settings, SpecialsFile and SpotGuardConfigFile, to use the default foldername of 'SpotGuard' (If you directly extracted the files, the folder would be called SpotGuard_A8). This is to make installing more intuitive, although if using another foldername for SG, you will still have to rename these variables.
     
    #11
    Ephoie and Taelyn like this.
  12. vannie

    vannie Ensign

    Joined:
    Apr 4, 2016
    Messages:
    5
    Likes Received:
    0
    Thanks Exacute for the help,
    I'm using the commandcrate_A8 and when running most all commands we get Server:Commandcrate:Executing Command on screen after the command has run, Would love to know how to silent the server response.
     
    #12
  13. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    I've had this suggested by another user a month or two ago.. I'm looking into the possibility to provide a 'global option' to disable/enable these.
    Truth be told, I've been a little busy with the wiki, and some project for CC, that will hopefully be possible with the current API ;)
    But it is on my todo.

    Untill then tho, you can add the tag 'SuppressExecNotice: true' to any command you don't wish to have output the message about it being executed.
    I would recommend however, to have some sort of user-feedback, that *something* happened.

    (For instance
    Code:
    Entries:
    - EntryKey: test
      SuppressExecNotice: true
      CommandEntry:
      - i: 0
        type: key
        key: test
      - i: 1
        type: int
        key: entity
        optional: true
        defaultvalue: "1001"
     
    #13
  14. SuperLiberty

    SuperLiberty Ensign

    Joined:
    Jul 28, 2018
    Messages:
    8
    Likes Received:
    1
    @Exacute Not sure where to ask for help. I am trying to use the ConsoleCommandSelf to initiate a AIManager event but it does not work. What is the proper format in my yaml?


    Code:
    Entries:
    - EntryKey: AlienInvasion
      HelpText: "Starts the Alien Invasion when 10 hours are played."
      Version: 1.0
      Creator: Superliberty
      CommandEntry:
      - i: 0
        key: invasion
        type: key
      Requirements:
      - AboveRep: 10
      Cost:
      - Reputation: 10
      Actions:
      - ExecuteMaximumAmountOfCommands: 2
      - ExecuteMinimumAmountOfCommands: 2
      - ExecuteCommandsAllowDuplicates: false
      - ExecuteCommand:
        - UniqKey: warningmessage
          BoxRed: "WE WILL NOT GO QUIETLY INTO THE NIGHT....WE WILL FIGHT!!"
      - ExecuteDelay: 30
      - ExecuteCommand:
        - UniqKey: spawncarrier
          ConsoleCommandSelf: aim av
      AttemptAtLogin: true
      AttemptToRunCommand: "invasion"
      AttemptToRunEveryInterval: 1800
      DisallowManual: true
      SuppressExecNotice: true
     
    #14
    Last edited: Nov 24, 2018
  15. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    This is fine. You can also hit me up on discord for further - Send me a PM if you can't find me in the server discord.

    Okay. So. Overall it looks good. Few things tho:

    -You don't need to do both Requirement and Cost - If you put something in cost, it will *take it* from the player AND check if the player 'can afford it'. It is fine that you have it as both, but you don't *need* to :) (Atleast not in this case as you require and take the exact same.. it might be relevant, if you only want to allow the player to do it, if they have more than .. 50? reputation or something, and make it cost 10).. Just a fyi, doesn't hurt.

    -You currently have multiple actions, where most of your actions doesn't actually do anything.
    ' - ExecuteMaximumAmountOfCommands: 2' : Doesn't have any associated commands, doesn't do anything
    ' - ExecuteMinimumAmountOfCommands: 2' : Same
    ' - ExecuteCommandsAllowDuplicates: false' : Same

    Also, these are not neccessary, if you have two, and want both executed. They are only neccessary if you have for example 2, and want to execute between 1 and 4 of them. Again, they're not hurtful, but neither neccessary :)
    However, in this case, it will fail, as you require two to be executed, and doesn't allow the same to be executed twice.
    Code:
      Actions:
      - ExecuteMaximumAmountOfCommands: 2
        ExecuteMinimumAmountOfCommands: 2
        ExecuteCommandsAllowDuplicates: false
        ExecuteCommand:
        - UniqKey: warningmessage
          BoxRed: "WE WILL NOT GO QUIETLY INTO THE NIGHT....WE WILL FIGHT!!"
    (Above would be the proper syntax, as they have an executecommand to .. 'work on')
    If the 'ExecuteCommandsAllowDuplicates' was true, it would give you two red boxes.

    .. Actually, since it fails, it will initiate an infinite loop. That's a bug. Gonna address that in the next version. Regardless, just do
    Code:
      Actions:
      - ExecuteCommand:
        - UniqKey: warningmessage
          BoxRed: "WE WILL NOT GO QUIETLY INTO THE NIGHT....WE WILL FIGHT!!"
    (You have one, and you just want it displayed once)

    '- ExecuteCommand:
    - UniqKey: warningmessage
    BoxRed: "WE WILL NOT GO QUIETLY INTO THE NIGHT....WE WILL FIGHT!!"' Should give you a red box


    Finally,
    Code:
      - ExecuteDelay: 30
        ExecuteCommand:
        - UniqKey: spawncarrier
          ConsoleCommandSelf: aim av
    'wait 30 seconds, then do: Console command 'aim av' '
    This should be fine.

    It would however appear that the game have an issue with this.. I'll report the bug, but don't hold your breath.. They are pretty much ignoring the API atm ..
    (I just tested some other commands like 'give credits 10', which works fine.. but it doesn't appear to want to do 'aim av')


    Anyway. Assuming that it wasn't caused by a game-bug, you would want to do
    Code:
    Entries:
    - EntryKey: AlienInvasion
      HelpText: "Starts the Alien Invasion when 10 hours are played."
      Version: 1.0
      Creator: Superliberty
      CommandEntry:
      - i: 0
        key: invasion
        type: key
      Cost:
      - Reputation: 10
      Actions:
        ExecuteCommand:
        - UniqKey: warningmessage
          BoxRed: "WE WILL NOT GO QUIETLY INTO THE NIGHT....WE WILL FIGHT!!"
      - ExecuteDelay: 30
        ExecuteCommand:
        - UniqKey: spawncarrier
          ConsoleCommandSelf: aim av
      AttemptAtLogin: true
      AttemptToRunCommand: "invasion"
      AttemptToRunEveryInterval: 1800
      DisallowManual: true
      SuppressExecNotice: true
    To accomplish what you wanted (Take 10 reputation from the player, if they can afford it, then give them a red-box message, wait 30 seconds, and 'spawn a ship', and do this at login, and again every 1800 seconds from the player logged in.

    A sidenote: You don't *HAVE* to use 'UniqKey' (One is automatically generated if none was used), but it is *highly recommended* if you do use 'min' and 'max' as you had before. Otherwise weird behaviour will likely occur.

    TLDR: few things you did that didn't work, but didn't really impact the result, but you can currently not do what you want, due to a gamebug

    Furthermore, from your helptext, I gather that you intend to execute this after 1800 seconds have passed, and then every time after this. Currently this is not possible (It will also be done when the player first logs in), I will add an option to not do it first time, but require the interval to have elapsed.
     
    #15
    Last edited: Nov 25, 2018
  16. SuperLiberty

    SuperLiberty Ensign

    Joined:
    Jul 28, 2018
    Messages:
    8
    Likes Received:
    1
    First off, thank you so much for your response and insight!

    Ok so I think I figured out another alternative for creating events using commandcrate and the PDA. I am going to put it in here for anyone else looking for a way to accomplish this.

    Code:
    Entries:
    - EntryKey: AlienInvasion
      HelpText: "Starts the Alien Invasion when 10 hours are played."
      Version: 1.0
      Creator: Superliberty
      CommandEntry:
      - i: 0
        key: invasion
        type: key
      Cost:
      - Reputation: 10
      Actions:
        ExecuteCommand:
        - UniqKey: warningmessage
          BoxRed: "WE WILL NOT GO QUIETLY INTO THE NIGHT....WE WILL FIGHT!!"
      - ExecuteDelay: 30
        ExecuteCommand:
        - UniqKey: spawncarrier
        - ConsoleCommandSelf: pda brief 27
      AttemptAtLogin: true
      AttemptToRunCommand: "invasion"
      AttemptToRunEveryInterval: 1800
      DisallowManual: true
      SuppressExecNotice: true
    This refers to pda chapter 27 to open. The player will have the option to activate the event or X out and essentially skip it.

    Code:
      - ChapterTitle: Chapter27
        Category: SoloMission
        Description: Chapter27_description
        PictureFile: "WelcomeBack.jpg"
        PlayerLevel: 1
        Visibility: WhenRewarded
        Activatable: Always
        AutoActivateOnGameStart: false
        CompletedMessage: Chapter27_completed
        Tasks:
          - TaskTitle: Chapter_Chapter27_tasktitle_0
            PictureFile: "WelcomeBack.jpg"
            Headline: Chapter27_tasktitle_0_headline
            Rewards:
              - Item: SathiumIngot
                Count: 999
              - Item: NeodymiumIngot
                Count: 999
              - Item: ZascosiumIngot
                Count: 999
              - Item: ErestrumIngot
                Count: 999
              - Item: GoldIngot
                Count: 999
              - Item: PromethiumPellets
                Count: 999
              - Item: ArmorHeavyEpic
                Count: 1
              - Item: DrillEpic
                Count: 1
            Actions:
              - ActionTitle: Chapter_Chapter27_tasktitle_0_actiontitle_0
                AllowManualCompletion: false
                CompletedMessage: "norm;6|Chapter_Chapter27_tasktitle_0_actiontitle_0_completedMessage"
                Description: Chapter_Chapter27_tasktitle_0_actiontitle_0_description
                Check: WindowClosed
                Amount: 1
                Names:
                  - Pda
            OnActivatePlayfieldOps:
              - "SpawnDrone:5;DroneBomberAttackBase;AttackStructureNearPlayer"
              - "SpawnDrone:10;DroneSmallFast01Rocket;AttackStructureNearPlayer"
              - "SpawnDrone:1;DroneTroopsTransport;AttackStructureNearPlayer"
              - "SpawnEnemy:10;ZiraxMale;10"
        NoSkip: true
        RepeatConditions:
          NumRepeats: 0
          Delay: 0
          DelayAdd: 0
        HideTasks: true
        Preamble: Chapter27_preamble
    
    and finally the pda.csv file for the above pda.

    Code:
    Chapter27,"Carrier Spawn lvl 1","","","","","","","","","","","","","","","",""
    Chapter27_description,"Defeat the Alien Invaders","","","","","","","","","","","","","","","",""
    Chapter27_completed,"PlaceholderTextCompleted","","","","","","","","","","","","","","","",""
    Chapter_Chapter27_tasktitle_0,"Defeat the alien invasion","","","","","","","","","","","","","","","",""
    Chapter27_tasktitle_0_headline,"Placeholder for 0(headline)","","","","","","","","","","","","","","","",""
    Chapter_Chapter27_tasktitle_0_actiontitle_0,"[c][ffffff][b]Defeat the Alien Invasion[/b][-][/c]","","","","","","","","","","","","","","","",""
    Chapter_Chapter27_tasktitle_0_actiontitle_0_completedMessage,"","","","","","","","","","","","","","","","",""
    Chapter_Chapter27_tasktitle_0_actiontitle_0_description,"Placeholder for 0(desc)","","","","","","","","","","","","","","","",""
    Chapter27_preamble,"Defeat the alien Invasion before they destroy you.\n\nYou will be well rewarded for completing this event.\n\nYou can choose not to take part in this event by simply 'X'ing out.  If you 'Activate' this event, you cannot cancel.\n\nThis event only occurs once every 10 hours of gameplay.\n\nGood Luck!","","","","","","","","","","","","","","","",""
    
     
    #16
    Exacute likes this.
  17. Slipstream

    Slipstream Captain

    Joined:
    Sep 22, 2016
    Messages:
    429
    Likes Received:
    964
    Would it be possible to add a new option? In addition to the "Playtime Greater Than", I'd like to see "Playtime less than or equal to".

    --Brian
     
    #17
  18. SuperLiberty

    SuperLiberty Ensign

    Joined:
    Jul 28, 2018
    Messages:
    8
    Likes Received:
    1
    @Slipstream Like this??

    1.8.10 - BelowPlaytime:
    (Double, Optional)
    Requires the executing player to have played (in minutes) less than this.

    1.8.11 - ExactPlaytime:
    (Double, Optional)
    Requires the executing player to have played (in minutes) exactly this.

    1.8.12 - AbovePlaytime:
    (Double, Optional)
    Requires the executing player to have played (in minutes) more than this.
     
    #18
  19. SuperLiberty

    SuperLiberty Ensign

    Joined:
    Jul 28, 2018
    Messages:
    8
    Likes Received:
    1
    The option that would be good is time since last death so that I can reset waves of challenges.... the longer you have lived the harder each wave gets until you die and it starts over to the first one again.

    I guess I could also just create a counter with a reset upon death, but I am not a good programmer...hehe
     
    #19
  20. Exacute

    Exacute Rear Admiral

    Joined:
    Feb 17, 2017
    Messages:
    456
    Likes Received:
    307
    The option for 'exactly' or 'less than' exist, and they can be chained together as an or requirement, if you want 'less than or equal to' :)
    (As SuperLiberty pointed out)

    I could prob. provide this. I'll add it to next version ;)

    Nice ;)
    Keep in mind however, that it still triggers first-time player logs on atm tho, so you may want to wait actually adding it to your server, untill next version, that will provide an option to not do that :)
    I hope they at some point provide greater PDA controll to the API, but atm console workarounds is the only way to start/reset/...
     
    #20

Share This Page