Currently the developers are putting their own money into JC2-MP to keep the servers online.

Please take a few seconds of your time and disable your AdBlock plugin for our website.

Ad revenue is not going to developers, it is used purely for covering our hosting costs.


You are also free to donate, which removes all ads from our website!

Patch 0.3 was just released! Full changelog here:

3 years ago

June 02, 2020, 12:40:43 pm

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Topics - SinisterRectus

Pages: [1] 2
Releases / luacheck globals table
« on: May 31, 2017, 10:24:05 pm »
If you use luacheck to lint your JC2MP files, then this might be useful for you. It is a list of definitions for JC2MP classes, enumerations, and functions. Simply drop it into any directory that contain your Lua files, or any directory above that one, recursively.

.luacheckrc file here

Code used to generate the list:

~ client ~
Code: [Select]
Events:Subscribe('ModuleLoad', function()
local globals = {}
for k in pairs(_G) do
if k:find('^%u') then
globals[k] = true
Network:Send('luacheck', globals)

~ server ~
Code: [Select]
Network:Subscribe('luacheck', function(globals)
for k in pairs(_G) do
if k:find('^%u') then
globals[k] = true
local sorted = {}
for k in pairs(globals) do
table.insert(sorted, k)
local f ='.luacheckrc', 'w')
f:write('globals = { -- luacheck: ignore\n')
for _, k in ipairs(sorted) do
f:write('\t"', k, '",\n')

Releases / [Release] Physics Engine - Bouncy Balls
« on: October 18, 2016, 02:00:19 am »
Physics Engine - Bouncy Balls

This is a networked, client-authoritative, sphere/ball physics engine scripted entirely in Lua for Just Cause 2 Multiplayer!

Video Demonstration

Check out this video to see how simulated spheres can interact with the game world and other game entities.


- Rigid bodies are currently limited to only spheres.
- Adjustable gravity and air density. Simulate moon physics if you'd like!
- Sphere properties are fully customizable:
    - Enable or disable dynamic movement or collisions
    - Hue, density, restitution, drag coefficient, friction coefficient, and radius on sphere creation.
    - Initial position, angle, linear velocity, angular velocity, and world can be specified.
- Client-authoritative physics simulation:
    - Discrete collision detection.
    - WorldNetWorkObjects are used to sync information across clients.
    - The client of the player that is nearest to a ball is the controller of that ball.


- To create a sphere, call the Sphere constructor in a server script with a table of arguments. The only required argument is a position.

    - position: Vector3
    - angle: Angle (default = zero angle)
    - dynamic: boolean (default = true)
    - collisions: boolean (default = true)
    - hue: number 0 to 360, inclusive (default = random)
    - density: number greater than 0 (default = 100)
    - restitution: number 0 to 1, inclusive (default = 0.6)
    - drag_coefficient: positive number (default = 0.47)
    - friction_coefficient: positive number (default = 0.3)
    - radius: number greater than 0 (default = 1)
    - linear_velocity: Vector3 (default = zero vector)
    - angular_velocity: Vector3 (default = zero vector)

- Other configurables are in shared\config.lua:

    - air_density: number in kg/m^3
    - gravity: Vector3 defining acceleration due to gravity
    - sync_interval: positive number (explained below)
    - icosphere_levels: positive number (# of polygons = 20 * 4^levels)

- Example scripts are provided where pressing Z will spawn a sphere in front of a player. Spheres are removed after 60 seconds of existence.


- Collision detection is discrete, not continuous. This means that very small and/or fast objects can tunnel through other objects or surfaces.

- The physics simulation is relatively demanding. One to two raycasts per frame per sphere are required for accurate world-collisions. Additional number crunching controls kinematics, entity collisions, and sync. You will not be able to simulate hundreds of objects at a time. On my machine (i5 4460 / GTX 970), I was able to run about 40 spheres before my FPS started to drop below 60.

- Sync is tuned to be conservative. The sync interval is measured in "meter-seconds" with a default of 3. If you plan to have a lot of objects, you may want to increase this. If you plan to have few objects, you may want to decrease this. With a value of 3, if a sphere moves 3 meters in 1 second, or 6 meters in 0.5 seconds, or 0.1 meters in 30 seconds, a sync update will be sent. Essentially, the faster a sphere is moving, the more frequently the updates will be sent. Note that, to reduce bandwidth, sphere angles are not synced.

- The engine does not account for low FPS or high ping. If a client is controlling physics with either, then you may see some unexpected results.

- Spheres are technically icospheres represented using the built-in Model class. They use a primitive shading algorithm designed to show rotation, not dynamic lighting or fancy textures. Although, I suppose you could texture the spheres if you were so inclined.

- For simplicity, players and vehicles are treated as spheres when they collide with sphere objects.

Thank You

Thank you to Dev_34 and Lord Farquaad for helping test the sync and thank you to F430 for participating in the video linked above!


Clone or download from GitHub here!


If I have left anything out of this post, or if you have questions, or are experiencing bugs or difficulties using this, please let me know below!

Releases / [Release] PathServer
« on: September 13, 2016, 05:16:01 am »

After over a year of on and off work, I am excited to finally present a relatively practical solution for server-authoritative AI pathfinding in Just Cause 2 Multiplayer. Hopefully at least a few people can make use of this package to augment their own AI projects.

Video Demonstration

In this video, I activated a stress test that you can find in example-server.lua. What you are seeing is a path being found approximately every 100 milliseconds.


Previous attempts at running pathfinding algorithms on a JCMP server by myself and Jaxm, JCMP developer, showed limited success. The algorithms worked, but due to the computation time required, running more than a few instances would significantly hinder the server's ability to run smoothly. The pathfinding could be done on clients, but this has its own disadvantages, and is not sufficient when the goal is server-authoritative AI. The ideal solution would be to combine the best of both situations: run the pathfinding local to the JCMP server, but offset the majority of the number crunching to another program.

In comes inter-process communication (IPC), something that Jaxm inspired me to pursue. In short, IPC allows programs to run independently, but still communicate with each other. Applied to JCMP pathfinding, such a set up would allow a JCMP server to make path requests to another running program. The server could continue operating normally while the path program works in the background. In addition to freeing up computation time, IPC allows for an opportunity to run the pathfinding on a platform other than Lua 5.2. Though practically any language could be used, I opted to continue the work that I had already started in Lua. The luvit platform, with which I am already familiar, allowed me to do this. Luvit provides a familiar Lua environment with built-in asynchronous I/O and improved performance over standard Lua.

How It Works

PathServer is split into two parts. One part is a luvit pathfinding package that can run independent of any JCMP software. The other part is a JC2MP module that facilitates interaction with the luvit process. Communication between the two is done via UDP where the luvit process is a server and the JC2MP server is a client to the luvit process.

The pathfinding itself uses an A* algorithm to find paths amongst a grid-like data structure. This grid is populated by data sourced from custom cell files. You can generate these files by yourself using my terrain mapping utilities. I have also provided 4 meter resolution files here.


- Visit and follow the instructions for your platform.

- Clone or download the PathServer repository from here. Install the PathServer (luvit) part to your favorite directory. Install the JC2MP part as you would install any other JC2MP module.

- Install cell files. The default location for this is a "cells" folder in the pathserver directory. Data with a 4 meter node size can be found here (47.8 MB 7z file).


- Adjust settings in config.lua. Defaults and brief explanations are provided there. If you have questions about each one, don't hesitate to ask for help.

- Run main.lua with luvit. It is configured to immediately initialize a UDP socket. No cell data is loaded until a path request is received via UDP.

- Boot up your JC2MP server and/or load the PathServer module.

- If you leave the example scripts as they are, your JC2MP module should connect directly to the luvit server.

- Start up a JC2MP client, join your server, aim at a position, and press P. You should be provided with a path to that position along with a confirmation message in chat.


In the JC2MP module, the API is quite simple. There is only one class (PathServer) and a few methods you should need to use:

Code: Lua
  1. PathServer:connect(ip, port)
  2. PathServer:getPath(start, stop, callback)

After intializing a PathServer instance, connect to the Luvit server using the connect method. The IP address is a string and the port is a number. You will probably want to use '' and 7780. These can be configured in the Luvit config file.

To request a path, use the getPath method. The start and stop parameters are Vector3 instances; the positions from which and to which you want to find a path. The callback is a function of your design, where the "args" table contains one of two values: either path (args.path), which is a table of vectors; the path from start to stop, or error (args.error), an error message if the path could not be found.

For example:

Code: Lua
  1. local pathServer = PathServer()
  2. pathServer:connect('', 7780)
  4. local player = Player.GetById(0)
  5. local start = player:GetPosition()
  6. local stop = player:GetAimTarget().position
  8. pathServer:getPath(start, stop, function(args)
  9.     if args.error then
  10.         print(args.error)
  11.     else
  12.         print('path length: ' .. #args.path)
  13.     end
  14. end)


- While the UDP connections are non-blocking, path requests are sent one at a time and do block other code from executing. This is generally not an issue, as requests should be fulfilled quickly. If you do experience lag, you can adjust some settings, reduce your request rate, or run multiple servers..

- There is a hardcoded limit to how far apart your start and stop positions are when pathfinding. If the two positions are not in the same cell, or are not in neighboring cells, then the request will be refused.

- With a 512 m cell size and 4 m node size, a typical cell will use 600 to 700 kilobytes of memory. You can run out of memory if you load too many cells, so there is a limit on the number of cells. Keep an eye on your memory usage, and adjust this limit as necessary.

- There is a limit to how many nodes will be searched in order to find a path. This is to prevent the program from wasting time searching for a path that does not exist. Adjust the value as you see fit, realizing that increasing it may increase your chances of finding a path, but will also slow things down if your AI is looking for bad paths.


If I have left anything out of this post, or if you have questions, or are experiencing bugs or difficulties using this, please let me know below! (And please don't forget that you need cell files to run all this!)

Timer Extension

Just a few utility functions modeled after Node.js timers. These are wrappers for timed event subscriptions that can easily be scripted manually, but might be more easily used with shortcuts.

Download from GitHub Here


Timer.SetImmediate(callback) - Schedules a callback to be called once on the next tick.
Timer.SetTimeout(delay, callback) - Schedules a callback to be called once following a delay in milliseconds.
Timer.SetInterval(delay, callback) - Schedules a callback to be called repeatedly, with an interval equal to delay.
Timer.Clear(timer) - Aborts a scheduled callback according to the timer handle returned by the above functions.
Timer.Sleep(delay) - Sleeps a coroutine for the given amount of milliseconds.
Timer.Tick() - Calls all waiting callbacks if their delays have elapsed. Automatically subscribed to PreTick.


Print "tick" every one second:
Code: Lua
  1. Timer.SetInterval(1000, function()
  2.     print('tick')
  3. end)

Alternate implementation using a coroutine:
Code: Lua
  1. coroutine.wrap(function()
  2.     while true do
  3.         Timer.Sleep(1000)
  4.         print('tick')
  5.     end
  6. end)()

Print the true time elapsed by the timer every second:
Code: Lua
  1. Timer.SetInterval(1000, function(args)
  2.     print(
  3. end)

Alternate implementation using a coroutine:
Code: Lua
  1. coroutine.wrap(function()
  2.     while true do
  3.         local args = Timer.Sleep(1000)
  4.         print(
  5.     end
  6. end)()

Print "tick" every second for 5 seconds:
Code: Lua
  1. for i = 1, 5 do
  2.     Timer.SetTimeout(1000 * i, function()
  3.         print('tick')
  4.     end)
  5. end

Alternate implementation using a coroutine:
Code: Lua
  1. coroutine.wrap(function()
  2.     for i = 1, 5 do
  3.         Timer.Sleep(1000)
  4.         print('tick')
  5.     end
  6. end)()

Start a self-destruct sequence any time a player enters a vehicle:
Code: Lua
  1. Events:Subscribe("PlayerEnterVehicle", function(args)
  3.     local player = args.player
  4.     local vehicle = args.vehicle
  5.     local color = Color.Red
  6.     local countdown = 10
  7.     local str = "Vehicle will self-destruct in: %i seconds!"
  9.     Chat:Send(player, string.format(str, countdown), color)
  11.     local timer = Timer.SetInterval(1000, function()
  12.         countdown = countdown - 1
  13.         Chat:Send(player, string.format(str, countdown), color)
  14.     end)
  16.     Timer.SetTimeout(countdown * 1000, function()
  17.         Timer.Clear(timer)
  18.         vehicle:SetHealth(0)
  19.     end)
  21. end)

Alternate implementation using a coroutine:
Code: Lua
  1. Events:Subscribe("PlayerEnterVehicle", function(args)
  3.     local player = args.player
  4.     local vehicle = args.vehicle
  5.     local color = Color.Red
  6.     local countdown = 10
  7.     local str = "Vehicle will self-destruct in: %i seconds!"
  9.     coroutine.wrap(function()
  10.         local n = countdown
  11.         for i = 1, n do
  12.             Chat:Send(player, string.format(str, countdown), color)
  13.             Timer.Sleep(1000)
  14.             countdown = countdown - 1
  15.         end
  16.         vehicle:SetHealth(0)
  17.     end)()
  19. end)


- This works on both the client and server from the shared folder. It must be placed in every module with which you wish to use it, or in your Lua autorun folder.
- The default delay unit is in milliseconds. If you want to use a unit other than milliseconds, change the GetTime definition at the top of the script.
- Time.Tick() is, by default, automatically subscribed to PreTick. You may disable this and manually call it from your own custom loop.
- At 100 ticks per second, a tick length is 10 ms and at 50 ticks per second, a tick length is 20 ms. Accuracy is not guaranteed if your timer delay is less than a normal tick length.

Download from GitHub Here

Work in progress / JC2MP Discord Bridge
« on: March 07, 2016, 10:31:00 pm »
Discord Bridge

Discord is a relatively new text and voice chat client. It's also free and multi-platform, but still in development.

This here is a module that connects the JC2MP chat to a Discord text channel.

To implement this, I originally wrote a self-contained Discord wrapper for JC2MP using LuaSocket + LuaSec, but there were problems with this. HTTP requests were locking up the JC2MP server, regardless of whether they were made asynchronous. I also couldn't find a good WebSocket library. After realizing that these problems would be difficult to overcome, I thought that any communication with Discord would have to be done with an external library, but for a time, the only idea for communication that I could come up with was writing and reading to and from a text file, which would have been a fantastic hack, but not at all efficient.

A few individuals had written libraries for building Discord bots in various languages, but none were written in Lua, with which I'm most familiar. So, I put this bridge on hold and went and wrote Discordia, Discord's first Lua library, written for the Luvit framework. Along the way, I learned a few tricks about networking, and libuv, the library that powers both Node.js and Luvit.

Now that Discordia has hit a stable state, I've revisited this bridge module. Knowing what I know now, I managed to implement a UDP link between Luvit and JC2MP, which can be used to relay messages between the JC2MP chat and a Discord bot running on Discordia. (If you'd like to use a library other than Discordia, you can, but you'd need to implement your own UDP server.)

Please let me know if there are any issues. Everything should be mostly stable, but realize that Discordia is in active development.

NB: Due to the low-level nature of this module, any errors in Lua code may crash your JC2MP server. Do not adjust the scripts unless you know what you're doing.

Download From Github

Video Demonstration

Releases / [Release] Server Physics - GetTerrainHeight
« on: March 02, 2016, 12:42:34 am »
Server Physics

This is a utility script that implements a version of Physics:GetTerrainHeight() on a JC2MP server. Height data is produced using bilinear interpolation of data sourced from a JC2MP client. The data in the included terrain.lua file was sampled at 64 meter resolution, providing an average terrain height inaccuracy of 0.003 meters.


Code: Lua
  1. ServerPhysics:GetTerrainHeight(Vector3)
  2. ServerPhysics:GetTerrainHeight(Vector2)

Download From GitHub Here

Work in progress / Random Maze Generator
« on: February 01, 2016, 04:19:31 pm »
I threw this together a few months ago and haven't worked on it since. It's not a finished product, but I think it's still pretty interesting, so I decided to share it here.

Random Maze Generator

This module includes scripts for randomly generating an interactive maze.

Mazes are generated by creating an instance of a Maze class on the server. This uses a WorldNetworkObject to sync mazes across clients with a few settings:

- Seed: Client-side assets are populated according to this number
- Position: Where the maze is located in the game world
- Size: Side-length of a maze in meters. Must be a power of 2 >= step.
- Step: Side-length of a cell in meters. Must be a power of 2 <= size.
- Height: How tall the walls of the maze are. Must be a power of 2 >= 2.

An example Maze is generated on ModuleLoad in the MazeManager script with position = (0, 201, 0), size = 128, step= 16, and height = 16.

In the client MazeManager script, whether ceilings are constructed can be toggled with the self.ceilings boolean.

As far as maze construction goes, a square grid is first created, and then a random path is carved through it using a depth-first search algorithm. Openings are also created at the northwest and southeast cells. The maze is then populated by various client entities.

A brick wall is placed where ever there is no open connection to a neighboring cell. This object is 4 meters long and 2 meters high, and what causes the power of 2 limitations in the maze settings.

Torches, complete with static object, fire effect, and light effect, are placed at every corner of the maze.

Floors and ceilings (if enabled) are added. A slight offset is used to prevent Z-fighting. Due to the finite size of the object used, if the step size is < 8, then only one object is required. If the step size is >= 8, then multiple objects are used per cell, depending upon the actual size of the cell.

If the step (cell size) is >= 8 and the maze height is >= 8, then large pillar objects and rubble fires are randomly placed throughout the maze. The fires do damage players who enter them.

If the step (cell size) is >= 8, then pickups are placed in all dead-ends. When contacted, the pickups are destroyed and a sound effect is played. They are not synced, though. This is one of the very next things that needs to be worked on.

After the maze is generated, all objects are spawned. These are buffered in order to prevent weird behavior such as invisible client effects or crashes. No more than 1 pickup, 1 rubble fire, 1 torch, and 1000 additional static objects are spawned per tick.

Let me know if you have any questions, suggestions, or comments about this.

Download From GitHub Here

Releases / [Release] SeaTraffic
« on: January 17, 2016, 09:30:35 pm »

This module is a companion to my previously released AirTraffic module. It populates Panau with various server-authoritative seafaring vehicles.

  • Boats travel randomly across all water that is sufficiently deep.
  • Turns are made possible by a combination of local path-finding and B├ęzier curves.
  • By default, only civilian boats are used.
  • The boats do not take bullet or explosion damage.
  • There is no collision management.

Video Demonstration.

Server Owners: Just like in AirTraffic, you will want to pay attention to the number of boats that are spawned and the update delay interval. Both are in the settings table in _init.lua in the shared folder. There is also a debug option, which toggles boat and path highlighting.

There are probably some tweaks to be made. If you run into any issues, or have any questions, please let me know!


Download From GitHub Here

Releases / [Release] Settlement Triggers
« on: January 01, 2016, 05:26:01 pm »
Settlement Triggers

*Visualizations not included

Download from GitHub here


- This is a utility script that uses ShapeTriggers from Just Cause 2's binaries to determine a player's proximity to each of Panau's mapped settlements; 369 in total.
- An event is fired for the entering and exiting of each settlement.
- PlayerEnterSettlement and PlayerExitSettlement fire on both the server and client.
- LocalPlayerEnterSettlement and LocalPlayerExitSettlement fire on the client.
- The firing of events can be toggled with the "fire_local" and "fire_network" variables.
- Arguments for the event callbacks are player and settlement.
- Accessible settlement properties are id, name, position, and angle.

Example Usage

This script could be used for creating or destroying location-specific assets, but here is a simpler example than that:

Code: Lua
  1. Events:Subscribe("LocalPlayerEnterSettlement", function(args)
  2.     Chat:Print(string.format("You have entered %s.",, Color.White)
  3. end)
  5. Events:Subscribe("PlayerExitSettlement", function(args)
  6.     Chat:Print(string.format("%s has exited %s.", args.player,, Color.White)
  7. end)


- Offset ShapeTrigger components do not appear to be working properly, so I split Panau City's trigger components into multiple triggers.
- Thanks to jaxm and SK8RJOSH for the settlement names

Releases / [Release] Autopilot
« on: December 19, 2015, 05:18:10 pm »
After many months of on-and-off development, and hundreds of crashed planes, I have decided to move my Autopilot module, my very first project, into a final release. The original topic can be found here.


Video Demonstration

Download from GitHub here


Autopilot Master

This main Autopilot button does not need to be pressed; activing any autopilot function will automatically turn the autopilot on. This switch is useful for turning everything off at the same time.

Basic Autopilot Functions

These are functions that control your plane at it's most basic level. Each of these directly manages input.

Roll Hold
- This attempts to lock your plane's roll or bank angle.
- Default min/max settings are 60 degrees in either direction.
- Press the "Zero" button to automatically level your plane's wings.
- Note that some planes may still drift left or right even with your wings leveled. Use heading hold if this is a problem.

Pitch Hold
- This attempts to lock your plane's pitch angle or angle of attack.
- Default min/max settings are 60 degrees in either direction.
- Press the "Zero" button to automatically level your plane's nose.
- Note that some planes may still lose altitude when your nose is pointed towards the horizon. Point it slightly up, or use altitude hold if this is a problem.

Speed Hold
- This attempts to lock your plane's airspeed.
- Default min/max settings are 0 and 500 km/h.
- There may be a delay in compensation when the plane is pitched at extreme angles.
- Press the "Cruise" button to set your speed hold to a pre-determined cruise speed. This is the speed that Just Cause 2 planes naturally adjust to while in level-flight with no thrust input.
- A maximum input level of 1 is required to takeoff from the ground. Anything lower will not move the plane.

Intermediate Autopilot Functions

These are functions that control your plane by automatically managing one of the three basic functions.

Heading Hold
- This attempts to lock your plane's heading by controlling its yaw angle.
- Since planes in Just Cause 2 do not have rudder controls, yaw is managed by making banked turns.
- Technical min/max settings are 0 and 360 degrees, though heading has no minimum or maximum.
- Heading hold uses roll hold, which cannot be deactivated while heading hold is active.
- Press the "Lock" button to automatically set your current heading as the desired target heading.
- Note that this does not necessarily hold a course towards a location. Use waypoint hold if this is a problem.

Altitude Hold
- This attempts to lock your plane's altitude above sea-level.
- Default min/max settings are 0 and 5000 m.
- In real life, planes do this by carefully balancing pitch and speed.
- Altitude hold uses pitch hold, which cannot be deactivated while altitude hold is active.
- You will need to manage your own speed, or manually configure speed hold to assist.
- Press the "Lock" button to automatically set your current altitude as the desired target altitude.
- Due to the actions of gravity and lift, your actual altitude may not perfectly match your setting.

Advanced Autopilot Functions

These are functions that control your plane by automatically configuring two or more of the basic and intermediate functions. They also require certain conditions to be activated.

Waypoint Hold
- This attempts to hold your plane's course towards a selected waypoint.
- If a waypoint is not set, this will not activate. Likewise, if a waypoint is cleared while this is active, it will deactivate.
- Waypoint hold uses heading hold, which uses roll hold, to fly towards a waypoint. It will not automatically control your altitude or speed.
- When a plane arrives at its waypoint, it will fly in a circular or figure 8 pattern above the waypoint until the waypoint hold is deactivated.

Approach Hold
- This attempts to land at a nearby airport.
- When you press the "Approach" button, all other autopilot functions will deactivate, and the autopilot will scan for runways. If your plane is close to, and aligned with one, the approach hold with activate.
- Once activated, this will use a combination of all intermediate and basic autopilot functions to fly your plane towards a smooth landing.
- This function is not perfect! A lot of its success depends on how far out you are from the runway and how stable your plane is when the approach begins. If you enter at a crazy angle, do not be surprised if your plane crash-lands.
- Any runway that is difficult to land at manually will be extra difficult to land at automatically.

Target Hold
- This will attempt to follow a targeted, occupied plane.
- When you press the "Target" button, all other autopilot functions will deactivate, and the autopilot will scan for nearby planes. If one is found, the target hold will activate.
- Once activated, this will use a combination of all intermediate and basic autopilot functions to follow the targeted plane at a distance of 100 meters.
- If the target is lost or if the targeted pilot bails out, target hold will deactivate.
- This is not meant for dogfighting or other fancy maneuvers. It works best with casual flight.

Debug HUD

Included with the Autopilot script is a very simple HUD. This is not required for the Autopilot script to work, but is a nice tool for visualizing some details about your plane. Feel free to include or exclude it with the module.


In-Game Settings
- In this window, you can manage units, gain, and input settings.
- Unit settings will propagate throughout the autopilot's GUI; however, all internal calculations are always done in meters, km/s, and degrees.
- Gain and input settings shouldn't be touched unless you know what you are doing.
- Gain is a multiplier for the input. The input setting is the *maximum* allowed setting used by each function.
- In general, lower settings will make the controls smoother, but may limit responsiveness, while higher settings will increase responsiveness, but may cause the plane to lose control.
- Adjusted settings are not saved. If you've messed something up, just reconnect to the server and the default settings will be restored.
- Server owners may adjust the defaults to suit their needs.

Other Adjustable Settings
- At the top of cAutopilot.lua, toggle buttons, the text color scheme, and whether to draw certain indicators can be adjusted.
- Adjusting other GUI settings is not supported.
- All other settings can be found in _init.lua. Do not touch these unless you know what you are doing.

Download from GitHub here

Releases / [Release] AirTraffic
« on: November 16, 2015, 06:18:25 pm »

This straightforward module populates Panau with fixed-wing air traffic at various altitudes.

  • By default, only civilian planes are used.
  • The planes do not take bullet or explosion damage.
  • Planes are destroyed and replaced after a client-side collision.

Video Demonstration.

Server Owners: Two settings that you will want to pay attention to are the number of planes that are spawned and the update delay interval. Both are in the settings table in _init.lua in the shared folder. There is also a debug option, which toggles plane highlighting.

If you run into any issues, like planes crashing into the ground or poor server performance, please let me know!


Download From GitHub Here

Releases / [Release] Terrain Mapping and Pathfinding Utilities
« on: October 20, 2015, 04:15:56 am »
    Terrain Mapping and Pathfinding Utilities

    In my NPC discussion, I talked about building a map of Panau and using it for pathfinding. I've decided to release a version of the script that I used to do this. For simplicity, I've excluded the utilities that I used to read and write data from and to the server.

    This script is not intended to be used by everyone, or on public servers. That said, if you are curious about how it works, I'd be happy to answer your questions.

    • cell_size = 128, -- int, power of 2
    • xz_step = 2, -- int, power of 2
    • y_min_step = 2, -- int, 1 to 4 recommended, 2 optimal
    • y_max_step = 1100, -- constant, do not change
    • ceiling = 2100, -- constant, do not change
    • sea_level = 200, -- constant, do not change
    • max_slope = 1, -- 1 is 45 degrees
    • map_sea = false, -- whether to bother with sea nodes
    • solid_sea = true, -- whether to map at sea level or ocean floor
    • eight = true, -- whether to implement 8-direction movement
    • path_height = 0.5, -- good compromise for pathing
    • graph_color = Color.Lime, -- color to use to render graph data
    • path_color = Color.Magenta, -- color to use to render path data
    • visited_color = Color.Cyan, -- color to use to render visited nodes

    • /getpos -- Returns LocalPlayer's raw world position.
    • /getcell -- Returns the currently occupied cell.
    • /tpcell <x> <y> -- Teleport to the specified cell. Both arguments are required.
    • /mapcell -- Generates nodes for the occupied cell and displays them.
    • /processcell -- Generates edges for any nodes in the occupied cell and displays them.
    • /start -- Saves and renders the node nearest to the LocalPlayer to use as the path-finding start node.
    • /stop -- Saves and renders the node nearest to the LocalPlayer to use as the path-finding stop node.
    • /path -- Finds a path from the start node to the goal node using an A* algorithm. Requires a start node and goal node to be selected on a processed graph.
    • /mem -- Returns how much memory the module is using in kB.
    • /unload -- Unloads all data.
    • /savecell
    -- Send cell data to server to save to a file
    • /loadcell
    -- Load cell data from server file
    • /automap
    -- Starts a mapping loop from the current cell


    Data Structure

    Most graph data is stored in the graph table self.graph.
    Nodes are represented by a table and indexed according to their cell and world positions. Notice that the order on the indices is x, z, y, not x, y, z:
    Code: [Select]
    self.graph[cell_x][cell_y][x][z][y] = {}
    By default, cell_x, cell_y, x, and z should always be integers. If they are not, you've done something wrong. The value for y can vary, but it is intended to be a number with no more than 2 decimal points, as dictated by the y_precision setting.

    In each table, information about the node is stored.
    The first entry is always the location of the node itself, stored as a vector for easy interfacing:
    Code: [Select]
    self.graph[cell_x][cell_y][x][z][y] = {Vector3(x, y, z)}
    For example:

    If you had a reference to a node such as
    Code: [Select]
    local node = self.graph[cell_x][cell_y][x][z][y]you could draw a circle at that node using:
    Code: [Select]
    Render:DrawCircle(node[1], 0.1, Color.White)since node[1] is that node's position.

    The second entry in the node table, node[2], is the node that is connected to the current node in the forward (negative z) direction, if such a connection exists. It is not a vector, it is a node!

    For example:

    If you had the same node above and wanted to draw a line from it to it's neighbor, you could use:
    Code: [Select]
    Render:DrawLine(node[1], node[2][1], Color.White)node[1] is node A's position. node[2] is node B, which neighbors node A in the forward direction. node[2][1] is node B's position.

    Following that example, all of the entries are:
    Code: [Select]
    -- [1] = center vector
    -- [2] = forward node
    -- [3] = backward node
    -- [4] = left node
    -- [5] = right node

    If you want to iterate through each node in the graph, you can use the following:
    Code: [Select]
    for cell_x, v in pairs(self.graph) do
    for cell_y, v in pairs(v) do
    for x, v in pairs(v) do
    for z, v in pairs(v) do
    for y, node in pairs(v) do
    -- do stuff with node here

    It's a little cumbersome, and reusing the same variable name may be confusing, but it works.



    Download From GitHub Here

    Releases / [Release] Vehicle Tuner
    « on: September 16, 2015, 02:58:33 am »
    Vehicle Tuner

    Today's 0.2 beta patch brought with it several new functions for tweaking vehicle physics. Super exciting, but also a bit confusing. To make lives a little easier, I put together this tool that lets you adjust all of the available properties of a land vehicle's transmission, aerodynamics, and suspension. Have fun!


    Download From GitHub Here

    Releases / [Release] Wingsuit
    « on: September 10, 2015, 06:23:37 am »

    "Why go over when you can go under?"

    If you haven't already read the announcement on Just Cause 2 Multiplayer 0.2, do it now!

    One of the example scripts seen in the announcement is Wingsuit, which demonstrates the use of new client-sided player velocity settings.
    Here's a video from dab88 trying it out on one of the 0.2 demo servers.

    After receiving some feedback, I've spent some time tweaking the script, and added some more features:

    • Rendered wings that adjust according to player color and the time of day
    • In the new default mode, the physics will be more like the wingsuit seen in Just Cause 3 previews. In this mode, you will slowly lose altitude as you glide forwards.
    • If superman mode is enabled, players can fly in any direction, and at variable speeds. This is the original version.
    • If grappling is enabled, then you can use a (custom) grappling hook to give yourself a quick boost. This is functional only if superman mode is not enabled.
    • Do a barrel roll!*
    • Smooth camera transitions.

    *There is only an animation for counter-clockwise rolls, so I used that for both left and right rolls.


    - In default mode:
    • Double-tap Shift while skydiving to deploy the wingsuit.
    • Double-tap Ctrl to retract the wingsuit
    • Use your grapple key to pull yourself along the ground

    - In superman mode:
    • Double-tap Shift to start flying
    • Double-tap Ctrl to stop flying
    • While flying, hold Shift to speed up
    • While flying, hold Ctrl to slow down

    -If rolling is enabled:
    • Double-tap Left to roll left
    • Double-tap Right to roll right


    Server owners can toggle settings in Wingsuit:__init():

    • Superman-flight, grappling, and rolling can be toggled with the self.superman, self.grapple, and self.rolls variables.
    • For superman mode, the maximum and minimum forward-speeds can be changed.
    • For default mode, the vertical-speed can be changed.
    • For both modes, the regular cruising speed can be changed.
    • For grapple mode, the tether length can be adjusted.

    Additional Credit To:

    • tally for some ideas and discussion
    • dab88 for showing off the wingsuit
    • Fkids for hosting the demo server
    • Avalanche Studios for the original concept


    Download From GitHub Here (JC2MP 0.2.0 or later required!)

    Releases / [Release] Extraction Map With Player Markers
    « on: July 08, 2015, 01:08:50 am »
    Extraction Map With Player Markers

    "How can I help you, hombre?"

    This is a custom PDA map that can be viewed in-game by pressing F2. The map will show markers for all players on the server, colored according to their player color. It will also allow the player to extract/fast-travel/teleport to any location on the map, and set native waypoints.


    • Press F2 to open or close the map. F1 and Esc will also close the map.
    • Left-click the map to extract to that location.
    • Middle-click the map to set a new waypoint or to remove an existing one.
    • Right-click the map to toggle player markers.

    Player Markers:

    Your own marker is always visible as a red target. There are three different settings for the markers of other players. The first and default setting is just the dot. Toggle once to show the player names. Toggle again to show the player vehicles, including their speed, altitude, and heading.

    Extraction Sequence:

    Left-click the map to summon a "helicopter" that will carry you to any location on the map.

    Video Demo of the Extraction Sequence.


    This script allows for server owners to adjust some parameters.

    • self.network_delay is how frequently player markers are updated. 1000 ms default.
    • self.extraction_enabled determines whether extraction is enabled. True by default.
    • self.extraction_speed is how fast the extraction helicopter moves. 5000 m/s default.
    • self.world_fadeout_delay is how long it takes for the world to fade-out before the extraction begins. 2000 ms default.
    • self.map_fadeout_delay is how long it takes for the map to fade-out after the extraction is completed. 2000 ms default.
    • self.world_fadein_delay is how long is takes for the world to fade-in after the map has faded out. 2000 ms default.


    Download From GitHub Here

    Pages: [1] 2