Skip to content

Commit

Permalink
Merge pull request #33999 from jbytheway/scores_via_events
Browse files Browse the repository at this point in the history
Implement scores via events
  • Loading branch information
kevingranade authored Sep 15, 2019
2 parents 85fb909 + 370630a commit fbdc8f3
Show file tree
Hide file tree
Showing 16 changed files with 880 additions and 47 deletions.
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

0 comments on commit fbdc8f3

Please sign in to comment.