Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement scores via events #33999

Merged
merged 10 commits into from
Sep 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions data/json/scores.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
[
{
"id": "avatar_id",
"type": "event_statistic",
"stat_type": "unique_value",
"event_type": "game_start",
"field": "avatar_id"
},
{
"id": "avatar_kills",
"type": "event_transformation",
"event_type": "character_kills_monster",
"value_constraints": { "killer": { "equals_statistic": "avatar_id" } }
},
{
"id": "num_avatar_kills",
"type": "event_statistic",
"stat_type": "count",
"event_transformation": "avatar_kills"
},
{
"id": "score_kills",
"type": "score",
"description": "Number of monsters killed: %s",
"statistic": "num_avatar_kills"
},
{
"id": "moves_not_mounted",
"type": "event_transformation",
"event_type": "avatar_moves",
"value_constraints": { "mount": { "equals": "" } }
},
{
"id": "num_moves",
"type": "event_statistic",
"stat_type": "count",
"event_type": "avatar_moves"
},
{
"id": "num_moves_not_mounted",
"type": "event_statistic",
"stat_type": "count",
"event_transformation": "moves_not_mounted"
},
{
"id": "score_moves",
"type": "score",
"description": "Distance moved: %s squares",
"statistic": "num_moves"
},
{
"id": "score_walked",
"type": "score",
"description": "Distance walked: %s squares",
"statistic": "num_moves_not_mounted"
},
{
"id": "avatar_takes_damage",
"type": "event_transformation",
"event_type": "character_takes_damage",
"value_constraints": { "character": { "equals_statistic": "avatar_id" } }
},
{
"id": "avatar_damage_taken",
"type": "event_statistic",
"stat_type": "total",
"field": "damage",
"event_transformation": "avatar_takes_damage"
},
{
"id": "score_damage_taken",
"type": "score",
"description": "Damage taken: %s damage",
"statistic": "avatar_damage_taken"
},
{
"id": "avatar_heals_damage",
"type": "event_transformation",
"event_type": "character_heals_damage",
"value_constraints": { "character": { "equals_statistic": "avatar_id" } }
},
{
"id": "avatar_damage_healed",
"type": "event_statistic",
"stat_type": "total",
"field": "damage",
"event_transformation": "avatar_heals_damage"
},
{
"id": "score_damage_healed",
"type": "score",
"description": "Damage healed: %s damage",
"statistic": "avatar_damage_healed"
},
{
"id": "avatar_headshots",
"type": "event_transformation",
"event_type": "character_gets_headshot",
"value_constraints": { "character": { "equals_statistic": "avatar_id" } }
},
{
"id": "avatar_num_headshots",
"type": "event_statistic",
"stat_type": "count",
"event_transformation": "avatar_headshots"
},
{
"id": "score_headshots",
"type": "score",
"description": { "ctxt": "score description", "str": "Headshots: %s" },
"statistic": "avatar_num_headshots"
}
]
81 changes: 79 additions & 2 deletions doc/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Here's a quick summary of what each of the JSON files contain, broken down by fo
| regional_map_settings.json | settings for the entire map generation
| road_vehicles.json | vehicle spawn information for roads
| rotatable_symbols.json | rotatable symbols - do not edit
| scores.json | statistics, scores, and achievements
| skills.json | skill descriptions and ID's
| snippets.json | flier/poster descriptions
| species.json | monster species
Expand Down Expand Up @@ -261,7 +262,7 @@ When adding a new bionic, if it's not included with another one, you must also a

### Item Groups

Item groups have been expanded, look at doc/ITEM_SPAWN.md to their new description.
Item groups have been expanded, look at [the detailed docs](ITEM_SPAWN.md) to their new description.
The syntax listed here is still valid.

| Identifier | Description
Expand Down Expand Up @@ -650,7 +651,83 @@ Mods can modify this via `add:traits` and `remove:traits`.
"post_terrain": "t_pit_spiked" // Terrain type after construction is complete
```

## Skills
### Scores

Scores are defined in two or three steps based on *events*. To see what events
exist and what data they contain, read [`event.h`](../src/event.h).

* First, optionally, define an `event_transformation` which converts events as
generated in-game into a format more suitable for your purpose.
* Second, define an `event_statistic` which summarizes a collection of events
into a single value (usually a number, but other types of value are
possible).
* Third, define a `score` which uses such a statistic.

#### `event_transformation`

Currently the only available transformation is to filter the set of events
based on certain constraints.

```C++
"id": "moves_on_horse",
"type": "event_transformation",
"event_type" : "avatar_moves", // An event type. The transformation will act on events of this type
"value_constraints" : { // A dictionary of constraints
// Each key is the field to which the constraint applies
// The value specifies the constraint.
// "equals" can be used to specify a constant string value the field must take.
// "equals_statistic" specifies that the value must match the value of some statistic (see below)
"mount" : { "equals": "mon_horse" }
}
```

#### `event_statistic`

A statistic must specify a source of events via one of the following:

```C++
"event_type" : "avatar_moves" // Consider all moves of this type
"event_transformation" : "moves_on_horse" // Consider moves resulting from this transformation
```

Then it specifies a particular `stat_type` and potentially additional details
as follows:

The number of events:
```C++
"stat_type" : "count"
```

The sum of the numeric value in the specified field across all events:
```C++
"stat_type" : "total"
"field" : "damage"
```

Assume there is only a single event to consider, and take the value of the
given field for that unique event:
```C++
"stat_type": "unique_value",
"field": "avatar_id"
```

#### `score`

Scores simply associate a description to an event for formatting in tabulations
of scores. The `description` specifies a string which is expected to contain a
`%s` format specifier where the value of the statistic will be inserted.

Note that even though most statistics yield an integer, you should still use
`%s`.

```C++
"id": "score_headshots",
"type": "score",
"description": "Headshots: %s",
"statistic": "avatar_num_headshots"
```

### Skills

```C++
"ident" : "smg", // Unique ID. Must be one continuous word, use underscores if necessary
Expand Down
1 change: 1 addition & 0 deletions doc/TRANSLATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ new syntax "name" would be a `dict`, which may break unmigrated script.
| Mutation names/descriptions
| NPC class names/descriptions
| Tool quality names
| Score descriptions
| Skill names/descriptions
| Bionic names/descriptions
| Terrain bash sound descriptions
Expand Down
3 changes: 3 additions & 0 deletions lang/extract_json_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ def warning_supressed(filename):
"colordef",
"emit",
"enchantment",
"event_transformation",
"event_statistic",
"EXTERNAL_OPTION",
"GAME_OPTION",
"ITEM_BLACKLIST",
Expand Down Expand Up @@ -147,6 +149,7 @@ def warning_supressed(filename):
"overmap_land_use_code",
"overmap_terrain",
"PET_ARMOR",
"score",
"skill",
"snippet",
"speech",
Expand Down
29 changes: 29 additions & 0 deletions src/event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ DEFINE_EVENT_FIELDS( gains_addiction )
DEFINE_EVENT_FIELDS( gains_mutation )
DEFINE_EVENT_FIELDS( gains_skill_level )
DEFINE_EVENT_FIELDS( game_over )
DEFINE_EVENT_FIELDS( game_start )
DEFINE_EVENT_FIELDS( installs_cbm )
DEFINE_EVENT_FIELDS( installs_faulty_cbm )
DEFINE_EVENT_FIELDS( launches_nuke )
Expand All @@ -136,4 +137,32 @@ DEFINE_EVENT_FIELDS( teleports_into_wall )

} // namespace event_detail

template<event_type Type>
static void get_fields_if_match( event_type type, std::map<std::string, cata_variant_type> &out )
{
if( Type == type ) {
out = { event_detail::event_spec<Type>::fields.begin(),
event_detail::event_spec<Type>::fields.end()
};
}
}

template<int... I>
static std::map<std::string, cata_variant_type>
get_fields_helper( event_type type, std::integer_sequence<int, I...> )
{
std::map<std::string, cata_variant_type> result;
bool discard[] = {
( get_fields_if_match<static_cast<event_type>( I )>( type, result ), true )...
};
( void ) discard;
return result;
}

std::map<std::string, cata_variant_type> event::get_fields( event_type type )
{
return get_fields_helper(
type, std::make_integer_sequence<int, static_cast<int>( event_type::num_event_types )> {} );
}

} // namespace cata
11 changes: 9 additions & 2 deletions src/event.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ struct event_spec<event_type::gains_skill_level> {
};

template<>
struct event_spec<event_type::game_over> : event_spec_empty {
struct event_spec<event_type::game_over> {
static constexpr std::array<std::pair<const char *, cata_variant_type>, 2> fields = {{
{ "is_suicide", cata_variant_type::bool_ },
{ "last_words", cata_variant_type::string },
Expand All @@ -407,7 +407,12 @@ struct event_spec<event_type::game_over> : event_spec_empty {
};

template<>
struct event_spec<event_type::game_start> : event_spec_empty {};
struct event_spec<event_type::game_start> {
static constexpr std::array<std::pair<const char *, cata_variant_type>, 1> fields = {{
{ "avatar_id", cata_variant_type::character_id },
}
};
};

template<>
struct event_spec<event_type::installs_cbm> {
Expand Down Expand Up @@ -547,6 +552,8 @@ class event
> ()( calendar::turn, std::forward<Args>( args )... );
}

static std::map<std::string, cata_variant_type> get_fields( event_type );

event_type type() const {
return type_;
}
Expand Down
Loading