The following is ideal; not all of these are being regarded at the moment.
- C++ source files use
.cpp
, header files use.hpp
. - Try to keep source files below 5,000SLoC. This makes a high-level overview of pieces of related functionality easier to parse.
- Break up large methods as much as possible, especially things like long
switch
statements.- Even better, see if there are ways to externalize data in declarative file formats, like item/character definitions.
- For large hunks of related code, split them up into files named with a common prefix. For example,
action.cpp
,action_dig.cpp
andaction_melee.cpp
. - We use
snake_case
for C++ code. - Prefix public methods inside C++ source files with the name of the file. For example,
enchantment.hpp
could containenchantment_add()
andenchantment_remove()
. This makes it easier to work out where the functions come from. Of course this needn't always be the case, for example if a large group of related functionality is spread out throughout related files. In that case, use the prefix of the main file (foraction_dig.cpp
, useaction_
). - Use names that make sense in English. Instead of
dmghp
, usedamage_hp
. - Use
#pragma once
inside headers. - Everything goes into the
elona
namespace. For major sections of the code, like the Lua functionality, an additional namespace could be added. For game logic, onlyelona
should suffice. - Avoid introducing new global variables in
variables.hpp
(there are already too many). If you absolutely must, consider encapsulating them inside an existing global variable. - Prefer
enum class
to raw integers for internal enumerations. - For data types that can be expanded with new members dynamically, like item IDs, use integers for legacy code or strings for new code with mods.
- Ideally all such IDs should use strings, to give a uniform interface.
- Avoid the usage of raw pointers unless absolutely necessary (which, in the current state of the code, is never). The rationale for this is none of the code currently checks for null pointers as a valid argument, instead relying on integer variables like
character.state
for existence (which are zeroed out when the character is deleted). Any code that introduces pointer arguments would have to also ensure their validity. - Avoid
new
anddelete
for game-related things. They aren't currently used there. - Pass references to objects to modify them inside a method.
- Use
const
where appropriate (arguments, methods, etc.) to make it clear what is being modified. - If you come across behavior that is correct but surprises you, be sure to leave a comment noting it, and bring it up in the respective PR.
- Run
clang-format
on your code by runningmake format
.
- Module/Table names should use
CamelCase
. Method names and variables should usesnake_case
. Classes exported from C++ should useCamelCase
prefixed withLua
, likeLuaCharacter
. - Anything related to the core API should go into a table inside the root
Elona
table. - Limit the amount of things in the global namespace as much as possible.
- Methods that mutate the state of a C++ reference object should be defined as methods, like
character:damage_hp(100)
. Methods that do not modify a C++ reference object should be defined as functions in modules, likeChara.is_ally(chara)
.- Methods that do not take a corresponding object as an argument, like map-related functions (whether or not they mutate anything) should also be defined as functions in modules.
- Constants should go into tables organized inside
Elona.Defines
. - You can shorten the amount of typing by declaring local "imports" like
local Enums = Elona.Defines.Enums
. - Be sure to document all public API methods/classes/definitions/etc., to make it easier for modders to understand how to use them. Remember that modders aren't necessarily programmers. Documentation for Lua uses the LDoc syntax.
- Ideally, provide working examples of how to use API methods also.
- You may have to look into the C++ source code to give accurate descriptions of how internal things like event callbacks work. If you change how things like events are triggered, be sure the corresponding Lua documentation is updated also.
These shouldn't be used in new code, but are useful for figuring out what things do.
cdata
- global character array (by character index)sdata
- global skill/resistance array (by character index)inv
- global item array (by item index), both for items on the ground and in character inventoriesibit
- global array of item bitflags (by item index)qdata
- global quest data arraygdata
- miscellaneous global variables array (seemacro_gdata.hpp
)mdata
- global map metadata array, like width/height (mdata(0)/mdata(1)
), global map/local map (mdata(2)
) and tileset (mdata(12)
)adata
- global map metadata array for things like quest completion, indexed by(data_type, map_id)
map
- the actual tile data of the map, indexed by(x, y, data_type)
. Adata_type
of0
is the wall/floor state.cc
- global current character index intocdata
for the character whose turn it is, the target of an action to a single character, etc. If this isn't passed in, it was implicitly set elsewhere.tc
- global target character index intocdata
for spell targets, etc.ci
- global current item index intoinv
x/y/sx/sy/dx/dy
- global variables used for positions.tlocx/tlocy
- global target location for magic, etc.mode
- global integer that changes the behavior of functions likectrl_inventory()
for querying the player's inventory, and various other things (there are lots of these, because HSP has no notion of local variables)f
- global success/failure flag - 1 for success, 0 for failurep
- global variable used for just about anything (indices, integer enumerations...). It's actually an integer array.rtval
- global integer or array return value (HSP is weird)fset<...>
- arrays of filter categories for quickly generating e.g. equipment, magic items, valuables like gems, etc.flttypemajor/flttypeminor/fixlv
- item generation global variables.dbid
- global integer ID for looking up item definitions for various purposes.inf_<...>
- UI constants.
flt()
- sets filters for random item creation withitemcreate
.DIM1, DIM2, DIM3
- macros for setting up integer arrays of dimension 1, 2, or 3.SDIM1, SDIM2, SDIM3
- same, but for string arrays.
elona_vectorX<T>
can be accessed by index to get references (p(0)
), or just the array name can be given and a reference to the item at the 0th index is implicitly returned (p
). The same works for assignment. HSP is weird.elona_vectorX<T>
will automatically resize if an index greater than its size is assigned to.