-
Notifications
You must be signed in to change notification settings - Fork 71
Levels
This part of the code has not yet been merged, but is fully supported by the AIMMO Unity project. The relevant pull requests are(in order) #220 and #224.
The intention of the level generation is to be as separate as possible from the rest of the AI:MMO implementation. The reason behind is that level requirements might change a lot on the way, so we want a as loosely as possible coupled module as part of the game logic.
The main class that gets exposed to the Django application is the JSONLevelGenerator, which implements the interface exposed by the BaseGenerator.
The BaseGenerator has the following template:
- contructor(setting)
- a set of basic settings that the map uses at generation
- see DEFAULT_LEVEL_SETTINGS in simulation.world_map
- get_game_state(avatar_manager)
- exposes a game state used by the turn manager daemon
- for details see Game State
- check_complete(game_state)
- returns false by default, should be overrided when inheriting
- function to check if a map is "complete"
- the turn manager runs the action for each avatar, then runs check_complete afterwards
- get_map (@abstract)
- returns the generated map
Generates a centered empty map by the width and height. Can also generate an empty map by given corners.
The JSON level generators generates a map in stages, receiving a JSON input map and a list of decoders that can modify the exposed map to the internal game classes. A complete reference of how the internals work can be found in World Map and Game Objects wikis.
Workflow:
- setup the metadata: map dimensions, etc.
- register the JSON that represents the map
- register the decoders that transform the JSONs into WorldMap objects
- decode the map applying the decoder to each of the JSONs
To register a level extend this class, this class is extended. All the levels described in the .txt and .json formats found in the levels folder are automatically imported.
The back-end levels are an easy way to convert a map described by one map and one(multiple) model(s) to a JSON level. The JSON format is the one fed to the Map Generator.
An example map.txt
look like this:
1 1 1 1 2 2
0 1 1 1 2 2
0 0 1 1 2 2
0 0 0 1 2 2
0 0 0 0 2 2
0 0 0 0 0 2
The .txt
files is a one-to-one representation of a map that is transformed in a JSON by applying a model to it. A model is a list of JSONS that are used to transform a .txt
map into a JSON. A model supports transforms, which are classes method calls. An example model looks like:
[
{
"code": "2",
"id" : "class:CellTransform.compute_id",
"x" : "class:CellTransform.get_x",
"y" : "class:CellTransform.get_y",
"sprite" : {
"width" : "400",
"height" : "400",
"path" : "Grass-400x400-isometric-top"
}
},
{
"code": "1",
"id" : "class:CellTransform.compute_id",
"x" : "class:CellTransform.get_x",
"y" : "class:CellTransform.get_y",
"sprite" : {
"width" : "512",
"height" : "1024",
"path" : "Obstacle-512x1024-isometric-top"
}
},
[...]
]
A transform is an instance of a class that can be called inside a model. A function can be called by prepending "class:" before the class name and function name. (e.g. class:CellTransform.get_x) E.g.:
{
"code": "1",
"id" : "class:CellTransform.compute_id",
"x" : "class:CellTransform.get_x",
"y" : "class:CellTransform.get_y"
}
The parser gets a level formatted as a 2D grid from numbers and transforms each number into a json representing that particular object.
A map is a *.txt file composed out of numbers. Each numbers represent a cell in the grid that will be eventually generated.
A model is an array of jsons. Each json has an associated code. By that associated code, the numbers in the map get translated into an json. To see how the final exported version of a map looks like, run levels.py
.
- parse_model
- gets the model name as a string and parsers the model from the folder models
- parse_map:
- changes the parser's associated map with map at the given path
- register model/s
- adds a new model to the model list
- register transform
- register an instance of a transform, so it can be used from *.json file
- map_apply_transfroms
- transforms a map formated as a list of numbers into a map formatted as list of jsons
- register_transforms(@abstract)
- register all transforms that can be used by the parser
The Unity levels get generated using the AI:MMO Unity Level Generation tool from this repository. These are already JSON (loosely) formatted and ready for exporting. A such example of a level looks like:
[
{
"x":-1.0000,
"y":2.0000,
"code":"ObstacleGenerator",
"sprite":{
"width":400,
"height":400,
"path":"Grass-400x400-isometric-top",
}
},
{
"x":0.0000,
"y":0.0000,
"code":"ObstacleGenerator",
"sprite":{
"width":512,
"height":1485,
"path":"brick-512x1485-isometric-windows-left",
}
},
[...]
]
We use the builder design pattern to generate levels from both formats. All the levels are automatically prepared for export by walking through the folders models, maps and json. A code snippet can be found below:
LEVELS = {}
TXT_LEVELS = os.listdir(_MAPS_FOLDER)
TXT_LEVELS.sort()
for lvl in range(1, len(TXT_LEVELS) + 1):
lvl_id = "level" + str(lvl)
LEVELS[lvl_id] = RawLevelGenerator().by_parser(CellParser()).by_map(TXT_LEVELS[lvl - 1]).by_models(["objects.json"]).generate_json()
JSON_LEVELS = os.listdir(_JSON_FOLDER)
JSON_LEVELS.sort()
for lvl in range(len(TXT_LEVELS) + 1, len(JSON_LEVELS) + len(TXT_LEVELS) + 1):
lvl_id = "level" + str(lvl)
LEVELS[lvl_id] = RawJSONLevelGenerator().by_json_level_name(JSON_LEVELS[lvl - len(TXT_LEVELS) - 1]).generate_json()