Welcome to the iw6x-client wiki!
This is the documentation for the IW6x scripting API.
Please note that the API might change over time and thus break existing scripts.
The intention is to create the best API possible and thus negelecting backwards compatibility.
There are also plans to create a common API with the Plutonium team, which might also have a huge impact on the API again, once this is done.
The IW6x scripting API integrates into the game's scripting VM/environment, which might be more commonly known as GSC. The scripts are written in LUA. In case you are not familiar with LUA, you should catch up here.
Here is a sample script. If you want to read code instead of documentation, this should cover up the basics of the API:
function move_helicopter(heli, player) local origin = player.origin origin.z = origin.z + 1000 heli:setvehgoalpos(origin, 1) end function setup_helicopter(heli, player) heli:setturningability(1) heli:setspeed(40, 15, 5) heli:setcandamage(false) local moveinterval = game:oninterval(function() move_helicopter(heli, player) end, 5000) function disconnect_callback() moveinterval:clear() heli:delete() end player:onnotifyonce("disconnect", disconnect_callback) end function spawn_helicopter(player) local origin = player.origin; local angles = player.angles; origin.z = origin.z + 1000.0; local heli = game:spawnhelicopter(player, origin, angles, "cobra_mp", "vehicle_battle_hind") setup_helicopter(heli, player) end function player_spawned(player) print("Player spawned: " .. player.name) player:freezecontrols(false) spawn_helicopter(player) end function player_connected(player) print("Player connected: " .. player.name) player:onnotifyonce("spawned_player", function() player_spawned(player) end) end level:onnotify("connected", player_connected)
All scripts need to be placed in the
iw6x/scripts folder in your Call of Duty: Ghosts installation folder.
You will need to create a folder for your script, for example
my_script. The main entrypoint is always the
__init__.lua file in there.
The structure should be like this:
COD Ghosts ├── iw6x │ ├── scripts │ │ ├── my_script │ │ │ ├── __init__.lua │ │ │ └── other_script.lua │ │ ├── my_next_script │ │ │ ├── __init__.lua │ │ │ └── script.lua └── iw6x.exe
To load another script, you can use the
If you want tofor example include
other_script.lua, use it like this:
include("other_script") -- [...]
There are only 5 distinct types needed to interact with game's scripting environment.
LUA of course has more types, but what is meant by this is the following:
When dealing with events, functions or fields, no complex LUA types are supported, only these primitive types:
Strings are text enclosed by quotes:
There is not much to say about them, as they are a common concept.
Integers are all whole numbers like
Boolean values, so
false, also fall under that category and are handled as
Floating point values are all numbers like
1.0 is also a floating point number literal. This is due to the dot notation, even though it's technically a whole number.
A vector is a type that groups exactly 3 floating point components. It is used to represent coordinates, angles or colors in the game.
Creating a new vector can be done like this:
local my_vector = vector:new(1.0, 2.0, 3.0) -- Statements below will be true assert(my_vector.x == 1.0) assert(my_vector.y == 2.0) assert(my_vector.z == 3.0)
You can access the individual components using either
z property accessors, or
b (note that both notations are equivalent, meaning
local my_vector = vector:new() -- vector is initialized as (0.0, 0.0, 0.0) my_vector.x = 5.0 my_vector.y = 1.0 my_vector.z = 2.0 local val1 = my_vector.x -- val1 is 5.0 local val2 = my_vector.r -- val2 is also 5.0, as x and r are equivalent -- The statements below are always true assert(my_vector.y == my_vector.g) assert(my_vector.z == my_vector.b)
Entities represent 'things' that 'exist' in the game. Players are for example entities.
Vehicles, hud elements, or the
level are also entites.
They can fire events, but also call functions into the game's scripting environment.
Some entites also have certain fields (or properties), like an
origin or a
Entites can fire events.
The concept was previously known as
waittill in GSC.
Popular events are the
connected event fired by the
level, or the
spawned_player event fired by players.
You can listen to any of these events by calling either
onnotifyonce on an entity:
function player_connected(player) -- [...] end level:onnotify("connected", player_connected)
In the example above, the
player_connected callback is called every time a player connects.
If you don't want to be notified every time, you can use
onnotifyonce instead, which only fires once.
If you called
onnotifyonce), but want to stop listening for notifications, you can call
clear on the object is returned:
-- [...] local listener = level:onnotify("connected", player_connected) -- [...] listener:clear() -- stops listening for the 'connected' event
Events can carry arguments. For example the
connected event by the
level carries the connecting player as an argument.
You can get these arguments by adding paramters to the callback functions, as seen in the
player_connected callback above.
To fire events, you can call the
notify function on an entity:
function player_connected(player) function my_callback() print("Yay") end player:onnotify("my_cool_event", my_callback) -- [...] -- This fires the 'my_cool_event' event player:notify("my_cool_event") end
You can also pass arguments to the event (there can be any amount, but they must be instances of one of the 5 primitive types):
function player_connected(player) function my_callback(arg1, arg2) print(arg1 .. " " .. arg2) end player:onnotify("my_cool_event", my_callback) -- [...] -- This fires the 'my_cool_event' event player:notify("my_cool_event", "Hello", "World") end
Timers are required to delay the execution. GSC had a function called
wait, which paused the execution of a thread for a specific amount of time.
Due to the control flow being different in LUA, there are neither threads, nor is there a wait function.
However, you can do something else to delay the execution. The functions
oninterval, which need to be called on the
game object, allow to do that. They take two arguments, a callback and a time to wait in milliseconds.
function callback() -- [...] end game:ontimeout(callback, 1000)
Similar to the functions
ontimeout executes the callback exactly once and
oninterval executes it every time the given amount of time passes.
You might want to cancel a timeout or an interval. This can be done exactly like it works for events.
oninterval return a timer with a
function callback() -- [...] end local timer = game:oninterval(callback, 1000) -- [...] timer:clear()
Timer callbacks don't take any arguments. If you want to pass an object, like a player, to a callback, you have to capture it, in a lambda for example:
function do_something(player) -- [...] end -- [...] local player = -- [...] game:oninterval(function() do_something(player) end, 1000)
The game provides a set of global functions and entity methods.
The functions provided are pretty much the same as COD4 had them.
You can have a look at a COD4 scripting reference: https://znation.nl/cod4script/ (libcod category doesn't exist in IW6x!)
Also note that all functions and methods are lowercase in IW6x!
Global functions are functions that don't need an entity to work. They need to be called on the
In the COD4 scripting reference, these functions don't have a
Call this on: paragraph.
Here is an example:
There are also member functions. These do have a paragraph called
Call this on: in the COD4 reference.
Here is an example:
For a list of all functions and methods available in IW6x, you can have a look at the source code:
Entities do have fields (or properties).
They differ for each type of entity. For example players have fields like
angles and hud elements have fields like
You can get and set them like this (only the 5 primitive types are supported):
local playername = player.name player.origin = vector:new()
Creating new properties or fields does not work yet, so you can't store own data in an entity.
You have to create your own construct for that.
Additionally, there is no complete list of which fields exist.
level.players is not a field for example. This was a variable.
So not every variable that existed in GSC is available as a field in here.
Unlike in GSC, the scripting API allows to execute console commands (for versions > v1.1.0).
You can use the
executecommand function on the
game object like this:
Players can disconnect. It might sound obvious, but is important for scripting.
Executing functions on a player that doesn't exist won't work. Additionally, captured player objects need to be freed.
GSC used to have a function called
endon, which allowed to terminate a thread as soon as an event happend, for example
stopped the execution, when a player disconnected.
There is no such concept in IW6x. You will need to work with a combination of clearing timers and
onnotifyonce, like this:
function do_something() -- [...] end local timer = game:oninterval(do_something, 1000) -- [...] player:onnotifyonce("disconnect", function() timer:clear() end)