Giter Site home page Giter Site logo

mangudai's Introduction

Mangudai

AoE2 Random Map Scripting for humans

JavaScript parser and linter for Random Map Scripts (RMS) for the Age of Empires II video game.

Install

Mangudai is published as an NPM package compatible with Node.js and browsers.

npm install mangudai

Use a tool like Webpack or Rollup to include Mangudai in your front-end app.

The code is compiled to ES5 (old and stable JavaScript) before publishing, so the module has maximum compatibility out-of-the-box.

Usage

Let's parse an RMS script into an Abstract Syntax Tree (AST) and lint it.

import { parse, lint } from 'mangudai'

const { ast, errors } = parse('<PLAYER_SETUP> \n random_placement')

if (errors.length) {
  console.log('Unable to parse the script! Probably invalid syntax.', errors)
} else {
  const lintErrors = lint(ast)
  console.log(`Linter found ${lintErrors.length} problems.`, lintErrors)
}

Mangudai is written in TypeScript and exports all relevant typings.

API

Contribute

Travis status Test coverage TypeScript Style Guide Standard Readme

This project is still in its early development stage. Any help is greatly appreciated! Feel free to ask questions in issues. PRs accepted.

License

MIT © Mangudai contributors

mangudai's People

Contributors

deltaidea avatar goto-bus-stop avatar greenkeeper[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

goto-bus-stop

mangudai's Issues

Add docs about RMS development process

  • Editors: Sublime Text and VS Code are officially supported
  • Grammar
    • Write a language manual
    • Add a decent AST spec
  • Data: research available commands, difference between AoC/HD/UserPatch datasets
  • Publishing: research Steam workshop and Voobly

Investigate using `start random` instead of `start_random`

Found this in MegaRandom Final.rms:

create_player_lands
{
...
start random
  percent_chance 60
   other_zone_avoidance_distance 3
  percent_chance 40
   other_zone_avoidance_distance 5
end random
}

Does the game allow to use start random and end random (without _)?

`if` should not require newline after condition

Hi, this is a super cool project!

I noticed one issue with the parser on the FenCrazy map, which is intended to test AIs in weird conditions, and IME is a decent test script for RMS tools as well :)

It errors on this snippet (and a few similar ones throughout the script):

if     NO_LEFT     #define NO_RIGHT
elseif SMALL_LEFT  #define SMALL_RIGHT
elseif MEDIUM_LEFT #define MEDIUM_RIGHT
elseif LARGE_LEFT  #define LARGE_RIGHT
elseif XLARGE_LEFT #define XLARGE_RIGHT
endif

saying "Unexpected token #define". The elseifs are apparently OK, when I add a newline after the if condition it parses fine.


Somewhat unrelatedly, that map also contains this strange construct: https://gist.github.com/goto-bus-stop/5a4ca7fc14fe5b380b8498632a89a578#file-fencrazyv6n1-rms-L3259-L3267

if KF_TC_NO_WALL
elseif KF_TC_P_WALL
 create_object PALISADE_WALL
elseif KF_TC_W_WALL
 create_object WALL
elseif KF__TC_F_WALL
 create_object FORTIFIED_WALL
endif
{
  /* block contents */
}

A standalone {} block parses OK in AoC, I think it treats the attributes as if they were a part of the previous block, because the AoC parser is a bit (very) quirky. It's probably fine not to support this in the parser and make authors fix their script instead.

Document all available built-in constants and flags

Should add a readable and programmatically available standard library.

Double-check that the following lists are complete, correct, sorted by ID, have no typos in descriptions:

AoC/HD built-ins

  • General flags (game modes, map sizes, ...)
  • Terrains
  • Units (with modifications like elite, packed, dead, ...)
    • Barracks units, including unique, but not those that are also available in castles
    • Archery range units
    • Stable units
    • Siege workshop units
    • Dock units
    • Castle units (unique units, petard, trebuchet)
    • Other common units (villager, monk, king, trade cart)
    • Heroes, campaign units
    • Cheat units
  • Buildings
    • Standard buildings
    • Other (yurts, bridges, ...)
  • Gaia and other entities
    • Animals
    • Trees
    • Other (relics, arrows, statues, rocks, ...)
  • Commands
  • Attributes

Expansions built-ins

  • General flags
  • Terrains
  • Units
  • Buildings
  • Other entities
  • Commands
  • Attributes

UserPatch built-ins

  • General flags
  • Terrains
  • Units
  • Buildings
  • Other entities
  • Commands
  • Attributes

  • Implement API to use the library in the linter and intellisense

Fix moo and nearley TypeScript definitions

  • nearley.Grammar.fromCompiled() - add this method correctly.
  • nearley.NearleyRule, etc. - think about consolidating types from compiled grammars and the package.
  • moo.compile / moo.states - allow { ..., error: boolean }, allow { ..., pop: boolean }.
  • Double-check everything.

Parse `start_random` - `end_random` blocks

Examples:

start_random
  percent_chance 50
    #define SUMMER /* 50% chance of being a "summer" map */
  percent_chance 50
    #define WINTER /* 50% chance of being a "winter" map */
end_random
start_random
  percent_chance 16 #define MEGAPACK_A
  percent_chance 16 #define MEGAPACK_B
  percent_chance 17 #define MEGAPACK_C
  percent_chance 17 #define MEGAPACK_D
  percent_chance 17 #define MEGAPACK_E
  percent_chance 17 #define MEGAPACK_F
end_random

Percents are always integers and may add up to less than 100, in which case the remaining chance is that nothing happens.

Allow negative numbers

Some attributes accept negative numbers:

create_land {
  clumping_factor -50%
}

This seems to cause extreme results in all such attributes. It's OK not to tell about negative numbers in the docs, but the parser should still be able to work with them.

Parse #include_drs directives, even though they can only be used in internal maps

Sometimes you can find lines like this:

#include_drs random_map.def 54000

These are used in the built-in random maps to load standard resources and stuff. They are ignored when used in custom maps. However, some people still copy them by mistake.

It might be a good idea to parse them with a warning. It should be possible to feed Mangudai any map, so this directive shouldn't cause an error.

Source: http://aok.heavengames.com/cgi-bin/forums/display.cgi?action=ct&f=28,40619,330,all

Allow to use `if` in command header

I've found this in Arena or LN_Refix7b.rms:

if TROPICAL_MAP
create_object JAVELINA
else
create_object BOAR
endif
{
  number_of_objects          1
  set_loose_grouping  
  set_gaia_object_only
  set_place_for_every_player
  min_distance_to_players    16
  max_distance_to_players    22
}

Fix badges in README

The repo has been moved from deltaidea/mangudai to mangudai/mangudai. Badges are dead now.

Allow comments around left curly of command block

Found this in a real RMS script:

create_object BOAR /* or JAVELINA */
{
     number_of_objects          2
     set_gaia_object_only
     set_place_for_every_player
     min_distance_to_players    14
     max_distance_to_players    18
}

Fix linter rule "no-redeclare": allow the same declaration to be in `if` and `else` is some cases

This should be allowed

if FOO
  #const BAR 1
else
  #const BAR 2
endif
if FOO
  #const BAR 1
  #define BAR
elseif QUX
  #const BAR 2
  /* BAR will still be undefined if !FOO && !QUX, so this is allowed. */
  #define BAR
endif
/* If declaration isn't the first statement in at least one case.  */
if FOO
  ...
  #define BAR
else
  ...
  #define BAR
endif

This should not be allowed

#const BAR 1
#define BAR

if FOO
  #const BAR 2
  #define BAR
endif
if FOO
  #const BAR 1
else
  #const BAR 1
endif
if FOO
  #define BAR
else
  #define BAR
endif

Linter should give an error when #define or #const are used in `if` or `random`

The game parses RMS in two steps: first the preprocessor directives (#const, #define, ...), then everything else. When you define something inside if or chance and that branch is not the one selected, the game chokes when parsing statements that use the thing you tried to define.

if FOO <-- imagine this is false
  #const BAR 123 <-- this is not defined
  create_terrain BAR { ... } <-- game crashes here
endif

Related: SiegeEngineers/aoe2map#2

Linter rules and what they need from AST/CST to be implemented

Here's a list of possible linter rules.
Most of these are inspired by ESLint built-in rules. Relevant ones are referenced by rule names.

Possible Errors

These rules relate to possible syntax or logic errors in random map scripts:

  • no-constant-condition — disallow unconditionally defined constants in conditions
  • no-dupe-attributes — disallow duplicate attribute names in commands
  • no-dupe-commands — disallow duplicate commands with exactly the same attributes
  • no-empty-else — disallow else blocks with no statements
  • no-empty-sections — disallow sections with no commands
  • no-include-drs — disallow use of #include_drs directive in custom maps
  • no-redeclare — disallow constant and flag redeclaration
  • no-unknown-names — disallow the use of non-standard commands, attributes, and undeclared constants
  • no-use-before-define — disallow the use of constants and flags before they are defined

Best Practices

These rules relate to better ways of doing things to help you avoid problems:

  • declarations-on-top — require constant and flag declarations be placed at the top of the script
  • no-implicit-empty-chance — disallow random blocks with chances that add up to less than 100
  • no-magic-numbers — disallow magic numbers in attribute arguments
  • no-multi-spaces — disallow multiple spaces

Stylistic Issues

These rules relate to style guidelines, and are therefore quite subjective:


Related to #3 and #1.

Allow empty `percent_chance` blocks

Found this in MegaRandom Final.rms:

create_player_lands
{
  ...
  start_random
    percent_chance 95
    percent_chance 5
      set_zone_by_team
  end_random
}

Although the empty percent_chance 95 is useless, it's still valid syntactically. At least I think so. Should support it.

Allow standalone commands before the first section

Here's a command that goes before the first section:

ai_info_map_type TYPE /* aoc */
ai_info_map_type TYPE N N N /* dlc and userpatch */

Docs: Optional command at the start of a script to tell the AI (computer player) the type of map.

The three numbers after TYPE are all 0 or 1: whether it's a nomad map (start without a TC), whether it's a michi map (teams are separated by forest), and an extra value that can be used by custom AI to detect your map.

If the map is not very similar to any of these maps, don't use ai_info_map_type.

Example: ai_info_map_type ARABIA means the map is like Arabia
In DLC/UserPatch: ai_info_map_type BLACK_FOREST 1 0 0 means it's Black Forest with nomad start.

Possible values of TYPE:

  • ARABIA
  • ARCHIPELIGO
  • ARENA
  • BALTIC
  • BLACK_FOREST
  • COASTAL
  • CONTINENTAL
  • CRATER_LAKE
  • FORTRESS
  • GHOST_LAKE
  • GOLD_RUSH
  • HIGHLAND
  • ISLANDS
  • MEDITERRANEAN
  • MIGRATION
  • MONGOLIA
  • NOMAD
  • OASIS
  • REAL_WORLD_[NAME]
  • RIVERS
  • SALT_MARSH
  • SCANDINAVIA
  • SCENARIO
  • YUCATAN

New values in DLC/UserPatch:

  • STEPPE
  • BUDAPEST
  • VALLEY
  • ATLANTIC
  • LAND_OF_LAKES
  • LAND_NOMAD
  • CENOTES
  • GOLDEN_HILL
  • MEGARANDOM
  • MICHI
  • AMBUSH
  • CUSTOM
  • NILE_DELTA
  • MOUNTAIN_PASS
  • SERENGETI
  • SOCOTRA
  • KILIMANJARO

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.