Ease the level on the third failure, before the quit
A player who installed this morning has failed level four three times in a row, and the game is loading it a fourth time unchanged. Difficulty is set per level in the balance sheet, so a level that is too hard for new players shows up in the funnel review and changes in the next patch. By then this player has gone. The fourth attempt is where the level needs to give a little, with a slower wave, an extra life or a boost dropped into the inventory.
level_failures_session- How many times they failed each level this session
level_attempts_session- How many times they started each level this session
levels_cleared_lifetime- Levels they have ever completed
Loads the fourth attempt with the easing your designers set for that level, a slower wave or an extra life, once this player's failures on it reach three this session and they are still in their first few levels, and leaves the level as designed for everyone else.
from datetime import timedelta
from snowplow_signals import Attribute, Event, EventProperty, StreamAttributeGroup, user_id
level_started = Event(name="level_started", vendor="com.example", version="1-0-0")
level_failed = Event(name="level_failed", vendor="com.example", version="1-0-0")
level_completed = Event(name="level_completed", vendor="com.example", version="1-0-0")
# Counted per level id, so the server looks up the level it is about to load.
level_failures_session = Attribute(
name="level_failures_session",
type="dict",
events=[level_failed],
aggregation="category_count",
property=EventProperty(vendor="com.example", name="level_failed", major_version=1, path="level_id"),
period=timedelta(hours=1),
)
level_attempts_session = Attribute(
name="level_attempts_session",
type="dict",
events=[level_started],
aggregation="category_count",
property=EventProperty(vendor="com.example", name="level_started", major_version=1, path="level_id"),
period=timedelta(hours=1),
)
levels_cleared_lifetime = Attribute(
name="levels_cleared_lifetime",
type="int32",
events=[level_completed],
aggregation="counter",
ttl=timedelta(days=365),
default_value=0,
)
# Players are logged in, so the group keys on the player's user id.
game_difficulty_retry = StreamAttributeGroup(
name="game_difficulty_retry",
version=1,
attribute_key=user_id,
owner="you@example.com",
attributes=[level_failures_session, level_attempts_session, levels_cleared_lifetime],
)// In the game server, when it builds the next level load
const player = await signals.getServiceAttributes({
name: "game_difficulty_retry",
attribute_key: "user_id",
identifier: playerId,
});
// { level_failures_session: { "4": 3, "3": 1 }, level_attempts_session: { "4": 4, "3": 2 }, levels_cleared_lifetime: 3 }
const failures = player.level_failures_session[levelId] ?? 0;
if (failures >= 3 && player.levels_cleared_lifetime < 10) {
level.load({ id: levelId, tuning: "eased", grant: "extra_life" });
}