0 of 0

File information

Last updated

Original upload

Created by

Sheepy

Uploaded by

ElleSheepy

Virus scan

Safe to use

Tags for this mod

About this mod

Allows Javascripts to be evaluated dynamically, whether from in-game console, through Modnix API, or in Modnix mod actions.

Requirements
Permissions and credits
Mirrors
Changelogs
Requires Modnix 3+.

This mod bundles and bridges a JavaScript runttime, so that mods can be written in JavaScript, a quick and easy scripting language.
JavaScript can run from in-game console, through Modnix API, or in form of Modnix Action mods.


Console as a Sandbox

The easiest way to start scripting is by using this with Debug Console (ver 7+).

After opening the console, enter the "JavaScript" command to enter the javascript shell.
Then everything you type will be evaluated as JavaScript on the fly.

For example,
1+2 should get you "3",
Api.call('mod_list') should get you a list of mod ids, and Repo.Get( GeoVehicleDef, 'PP_Manticore_Def' ).BaseStats.Speed.Value
would return the speed of Manticore.
(Note that BaseStats.Speed is a C# struct, which restricts how you can modify it. Welcome to modding.)

If you prefer Console Enabler, it does not provide console shells, i.e. there is no "JavaScript mode".
You may still run one-lined script, e.g. JavaScript '"Foobar".substr(3)'

Remember, you can copy and paste in the console input with Ctrl+C and Ctrl+V.


Making Your Mod

Once you are happy with your scripts, you can put them into a plain-text mod.

See Laser On Fire (code) for a short example.

You can also call the eval.js API to evaluate and get the result of javascript.
For example, Dump Data allow users to define extra data to extract through JavaScript.


Why JavaScript?

Because this is the cleanest solution I found.

This mod bundles the V8 engine used by Google Chrome browser (except on iOS), called through Microsoft ClearScript.
And ClearScript is just a wrapper. Nothing (too) fancy.

My previous attempt, C# Scripting Library, depended on Rosyln, which depends on Dynamic Language Runtime and .Net Standard, which is not only heavy and creates lots of dynamic assemblies that stay in memory forever, but must also override system libraries. After a few game updates, the conflicts can no longer be resolved.

There are other alternatives such as IronPython and IronRuby. They seem to be lighter than Rosyln, so may be a modder can make them works.
Me? I am happy with JavaScript.


PPML Support?

Not planned because I don't think it is important.   Lots of mods to update, and I haven't play Phoenix Point at all.
You can fork and contribute code, like how I updated PPDefModifier.   We can't be more happy to have helps.

Note that it is non-trivial.  If it is trivial, you are doing it wrong.   For example, you need to reimplement console.  It is not as simple as string.Format.


C# to JavaScript and back

ClearScript is a great library.  Microsoft have made sure that most concepts are transparently translated.
For example, generic methods just require you to pass in a type as the generic parameter, e.g. Repo.Get( GeoVehicleDef, "name" )
IEnumerables from C# can be looped with for..of, e.g. for ( let v of Repo.getAll( GeoVehicleDef ) ) console.log( v )

For more details, read the ClearScript FAQtorial.   The ExtendedHostFunctions object can be accessed as host and xHost, so most examples should work as long as you do them in order.

Core system classes can be accessed from the DotNet namespace, e.g. DotNet.System.Guid
Game classes are already imported, but can also be access from the Game namespace, e.g. Game.Base.Core.GameUtl
Unity CoreModule classes are imported since 2.1, and all Unity classes can be accessed from the Unity namespace.
Other classes can be imported through the host / xHost global object.


Scripting Helpers

You can access these helpers from within JavaScript. They are thread-safe.
You can also access all game types without writing their namespace - they have already been "imported".

All helpers have both C# style methods (GetAll) and JS style methods (getAll), except console which only have lowercase methods.
The Repo and Damage helpers may be moved into an independent mod in the future, but there is no roadmap yet on when that may happen.

console
Offers basic console methods such as console.log, .error, .warn, .debug (maps to verbose level), .trace etc.
If you want to log a formatted message, you must use Unity's tags and C# composite formatting. e.g. console.log( '<color=blue>{0}</color>', 123 )

Log
.error - Log message at error level.
.warn - Log message at warning level.
.info - Log message at information level.
.verbo / Log.verbose - Log message at verbose level.
// Since version 2.2, console and Log methods will use the calling mod as context (prefix), instead of JavaScript runtime.

Api
.call( string, object... ) - Call Modnix API.
.call( type, string, object... ) - Call Modnix API and cast result to specific type. Will throw error if the cast failed.

Espy
.gameAssembly - Get the game's assembly.
.getType( string ) - Find and return a type by name. Null if not found.
.getType( type ) - Return the .Net Type object of the HostType.
.field( type, string ) - Get the static field value of a type.
.property( type, string ) - Get the static property value of a type.
.call( type, string, ... object[] ) - Call the static method of a type and return its result.
.create( object ) - Create a espy proxy instance for this object.
.create( type ) - Create a espy proxy instance for this type.
.anyBinding => BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
.objectBinding => BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
.staticBinding => BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic
// extensions
Type.field( string ) - Find first field of the given name (any binding), returns FieldInfo.
Type.fields( string ) - Find all fields of the given name (any binding), returns IEnumerable<FieldInfo>.
Type.method( string ) - Find first method of the given name (any binding), returns MethodInfo.
Type.methods( string ) - Find all methods of the given name (any binding), returns IEnumerable<MethodInfo>.
Type.propety( string ) - Find first property of the given name (any binding), returns PropertyInfo.
Type.propeties( string ) - Find all properties of the given name (any binding), returns IEnumerable<PropertyInfo>.
object.espy() - Create a espy instance which helps getting members, setting members, and invoking methods. (See below)
object.espy( string ) - Get the value of a field or property.
object.espy( string, object ) - Set the value of a field or property. Return true on success, false on failure.

Espy instance
.field( string ) - Get the field value of this instance's object or type.
.field( string, object ) - Set a field value of this instance's object or type.
.property( string ) - Get the property value of this instance's object or type.
.property( string, object ) - Set a property value of this instance's object or type.
.invoke( string, ... object[] ) - Call the method of this instance's object or type.
.fields () - return a FieldInfo[] of all fields of this instance's (object's) type.
.properties () - return a PropertyInfo[] of all properties of this instance's (object's) type.
.methods () - return a MethodInfo[] of all methods of this instance's (object's) type.

Patch
.prefix( type, method, function ) - Add a Harmony prefix patch to the type's method. The function will receive a PatchEvent as parameter.
.postfix( type, method, function ) - Add a Harmony postfix patch to the type's method. The function will receive a PatchEvent as parameter.
.prefixReturn( type, method, function ) - Add a Harmony prefix patch to the type's method, capturing its return value. The function will receive a PatchEvent as parameter.
.postfixReturn( type, method, function ) - Add a Harmony postfix patch to the type's method, capturing its return value. The function will receive a PatchEvent as parameter.
.unpatchAll() - Remove all patches added by current mod.

PatchEvent instance
.instance - the object owning the called method. Non-arrow functions may also use 'this' instead. Readonly.
.targetMethod - the patched method, a MethodInfo. Readonly.
.canAbort - true if subsequence patches and the original method can be aborted. Readonly.
.hasResult - true if the result can be modified through this event object. Readonly.
.result - the result object. Set this to change the method's result, but will throw exception if hasResult is false.
.abort() - Skip subsequence patches and the original method, if possible. Only possible for prefix patches.
.aborted - true if abort() was called and canAbort is true.
// Note that it is impossible to dynamically capture call parameters. That must be done with C#.

Repo
.repo - Direct access to the game's DefRepository.
.get( string ) - Find a BaseDef from guid, name, or path. Name and path are cached and should be faster than you doing the seek.
.get( type, string ) - Find a Def from guid, name, or path. type must be a subclass of BaseDef. You usually want to use this method.
.getAll( string or type ) - Find all BaseDef of a given name, path, or type.
.getAll( type, string) - Find all Def of a given name or path of given type. Type must be a subclass of BaseDef.

Damage
.Parse( string ) - Find DamageKeywordDef from string - acid, blast, bleed, fire, goo, mist, paralyse, pierce, poison, psychic, shock, shred, sonic, syphon/vampiric, viral, virophage, and normal. Default to normal if no name supplied.
// extensions
WeaponDef.getDamage( string / DamageKeywordDef = "normal" ) - Get the weapon's damage number (float) of a specific damage type.
WeaponDef.setDamage( float ) Set the weapon's normal damage and return the weapon (for call chains).
WeaponDef.setDamage( string / DamageKeywordDef, float ) Set the weapon's damage and return the weapon.
WeaponDef.addDamage( string / DamageKeywordDef, float ) - Add damage number and return the new damage.
WeaponDef.exchangeDamage( string / DamageKeywordDef, float ) - Set damage and return original damage.
WeaponDef.compareExchangeDamage( string / DamageKeywordDef, float, float ) - Set to first damage if the original matches the second, and always return the original.
WeaponDef.acid() - Get Acid damage. Same for Blast / Bleed / Damage / Fire etc.
WeaponDef.acid( float ) - Set or remove Acid damage, and return the weapon. Same for Blast / Bleed / Damage / Fire etc.

API

These API may be called by other mods through Modnix.  If Debug Console is installed, they may also be called through the "modnix" console command.

Like helpers, the pp.def and pp.defs APIs may be moved out in the future, but as of writing there is no roadmap.

eval.js
Param: string
Return: object
Evaluates parameter as JavaScript and return the result. Each calling mod get their own context which is not shared with other mods.

pp.def
Spec: null, "guid", "name", or "path". Unrecognised spec will be regarded as null.
Param: string or Type
Return: BaseDef
Find the first BaseDef in the game's DefRepository whose guid/name/path/type matches the given parameter. If no spec is provided, the api will intelligently detect the spec from the param.

pp.defs
Find the first BaseDef in game's DefRepository whose guid/name/path/type matches the given parameter. If no spec is
provided, the api will intelligently detect the spec from the param.
Spec: null, "guid", "name", or "path". Unrecognised spec will be regarded as null.
Param: string or Type
Return: IEnumerable<BaseDef>
Find all BaseDef in the game's DefRepository whose guid/name/path/type matches the given parameter. If no spec is
provided, the api will intelligently detect the spec from the param.