Skip to content
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
11 changes: 6 additions & 5 deletions docs/CONTRACTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -449,14 +449,14 @@ Viewer sprites (server/sprites.ts):
between them in 24 h already): no Elo or dollars."
- An active duel with no action for `inactivityMs` is cancelled without scoring. Cancelled duels
return both players to maxHp.
- Upgrades (server/upgrades.ts): every win earns one point and check-ins add more (`bonus`), at most 20 together, spent on power (+4% damage dealt per point), guard (-4% damage taken) and luck (4% per point that a quick-duel miss lands as a hit), at most 8 each. Profiles carry `bonus` (check-in points), `checkins` and `streak`; the profile answer's `upgrades` has `points`, `fromWins` and `fromCheckins`. Points can be moved while the fighter is not in a duel. Hats are cosmetic; some unlock at a number of wins. A rank reset clears Elo and wins but keeps `bonus`.
- Upgrades (server/upgrades.ts): every win earns one point and check-ins add more (`bonus`), with no cap. Points buy levels in power (damage dealt), guard (damage taken) and luck (the chance a quick-duel miss lands as a hit). Levels 1-8 cost 1 point each and give 4% each; the cost doubles every 8 levels (9-16 cost 2, 17-24 cost 4), and above level 8 the effect closes in on a ceiling (power and guard 60%, luck 50%; level 16 gives about 44.2%). Stored levels stop at 200. The channel's `maxStatLevel` caps the levels that count in its duels; levels above it stay saved. A rank reset that leaves too few points takes levels off luck, then guard, then power. Profiles carry `bonus` (check-in points), `checkins` and `streak`; the profile answer's `upgrades` has `points`, `fromWins`, `fromCheckins` and `maxLevel`. The fighter page offers suggested builds (`PRESETS`) that fill the levels from the points; the viewer still saves. Points can be moved while the fighter is not in a duel. Hats are cosmetic; some unlock at a number of wins. A rank reset clears Elo and wins but keeps `bonus`.
- Dollars: every finished ranked duel pays `winDollars` to the winner and `lossDollars` to the loser, saved fighters only. game.ts records it as `duel.payout` and the room adds it to `profiles.dollars` once, in the same transaction (`duel.paid`). Like Elo, a payout stays out of `!wallet`, `/api/profile` and the leaderboard until the stream has shown the fight. Profiles carry `dollars` (at most 1,000,000). Check-ins give points, not dollars. A rank reset, a profile save and a re-save keep dollars; removing a viewer deletes them.
- `!wallet` (the StreamElements `wallet` action, also while duels are paused): "@x: $N PixFray dollars, P of 20 upgrade points, S-stream streak." Reasons: `wallet`, `no_fighter`.
- Pets and the shop (server/pets.ts): 14 built-in pets in 5 tiers, plus up to 24 uploaded per channel. A fighter owns any number of pets (`owned_items`) and brings one (`profiles.pet`); hats can be bought too. A pet's boost is added after the upgrade limits: common and uncommon +1 to one stat, rare +2, epic +2 and +1 to a second stat, legendary +1 to every stat. Pets cost `petPrice<Tier>` dollars; a hat whose wins aren't reached costs `hatPricePerWin` times the wins it needs. The overlay draws the pet behind its fighter at about 40% of its height; epic and legendary pets sparkle, legendary pets hover. Snapshot players and `/api/looks` carry `pet` and `petTier`; players also carry `petBoost`.
- `!wallet` (the StreamElements `wallet` action, also while duels are paused): "@x: $N PixFray dollars, P upgrade points, S-stream streak." Reasons: `wallet`, `no_fighter`.
- Pets and the shop (server/pets.ts): 14 built-in pets in 5 tiers, plus up to 24 uploaded per channel. A fighter owns any number of pets (`owned_items`) and brings one (`profiles.pet`); hats can be bought too. A pet's boost is added after the channel's `maxStatLevel` cap: common and uncommon +1 to one stat, rare +2, epic +2 and +1 to a second stat, legendary +1 to every stat. Pets cost `petPrice<Tier>` dollars; a hat whose wins aren't reached costs `hatPricePerWin` times the wins it needs. The overlay draws the pet behind its fighter at about 40% of its height; epic and legendary pets sparkle, legendary pets hover. Snapshot players and `/api/looks` carry `pet` and `petTier`; players also carry `petBoost`.
- Cosmetics and builds (server/cosmetics.ts, drawn by src/served/cosmetics.ts): recolor (a CSS filter on the fighter), pet color (the same 8 tints on the pet), accessories, walking trails, win effects (about 3 s over the winner when the overlay shows the result), win taunts (a speech bubble then) and titles (under the nameplate). Each kind has one config price. Owned cosmetics are `owned_items` rows; the worn ones are profile columns. Build slots: 1 free, the 2nd costs `buildSlotPrice`, the 3rd to 5th `buildSlotPriceMore` each. Each slot keeps its own loadout in the `builds` table; saving to a slot makes it the one on stream, and every build can spend the full point pool. Removing a viewer deletes their builds; a rank reset keeps them.
- `!pet [@name]` (the `pet` action): "@x's pet: Fox (rare), +2 power." Reasons: `pet`, `no_pet` (with the site link for the sender's own), `no_fighter`.
- `!pay @name amount` (the `give` action; on a channel the PixFray bot reads, it's `!give` and `!pay` stays an alias; `a=` is `20` or `$20`): moves dollars between two saved fighters while the channel is live (the same Twitch check as check-ins). In order it refuses with `give_off` (config `giveEnabled` false), `no_fighter`, `give_usage` (no target or no whole amount of at least 1), `target_not_found`, `self_give`, `twitch_error`, `not_live`, `too_few_duels` (fewer than `giveMinDuels` finished duels, `{duels, need}`), `give_cap` (already gave `giveMaxPerStream` this stream), `over_cap` (`{left}`), `not_enough` (`{dollars}`) and `target_full` (`{to, room}`: the receiver's wallet can't hold the amount, so nothing moves). Hidden duel results and payouts don't count yet. The limit is per Twitch stream id (`profiles.give_stream`, `given_in_stream`). Success is `given` with `{amount, to, dollars, left}`; the command log stores the amount.
- Check-ins (the StreamElements `checkin` action; the test site's dev chat also takes `!checkin`, Twitch EventSub chat does not): once per stream, only while Twitch's Helix `/streams` says the channel is live (cached 60 s; a Twitch error answers "try again" and is not cached). Gives `checkinPoints` (bonus capped at 1000). The streak counts the channel's streams in a row that had any check-in; one missed stream is forgiven once every 7 days. With `streakBonus`, a streak of 3, 7, 14 or 30 gives +1 more. Reasons: `checked_in`, `already_checked_in`, `not_live`, `no_fighter`, `twitch_error`. On the test Worker, `POST /api/devtools/<ch>/live` with `{live, streamId?}` (DEV_TOOLS_TOKEN only) replaces the Twitch answer: `true` is live, `false` is offline, and `null` goes back to asking Twitch.
- Check-ins (the StreamElements `checkin` action; the test site's dev chat also takes `!checkin`, Twitch EventSub chat does not): once per stream, only while Twitch's Helix `/streams` says the channel is live (cached 60 s; a Twitch error answers "try again" and is not cached). Gives `checkinPoints` (bonus capped at 1,000,000). The streak counts the channel's streams in a row that had any check-in; one missed stream is forgiven once every 7 days. With `streakBonus`, a streak of 3, 7, 14 or 30 gives +1 more, 60 gives +2, 100 gives +3, 200 gives +5 and 365 gives +10. The reply names the total and what is unspent: "@x checked in: +2 points (7-stream streak, streak bonus). 18 points, 3 unspent. Spend them at <link>", or "…, all spent." with no link. Reasons: `checked_in`, `already_checked_in`, `not_live`, `no_fighter`, `twitch_error`. On the test Worker, `POST /api/devtools/<ch>/live` with `{live, streamId?}` (DEV_TOOLS_TOKEN only) replaces the Twitch answer: `true` is live, `false` is offline, and `null` goes back to asking Twitch.
- Ranked play needs a saved profile (`ranked_sign_in_required`).
- Reason codes: `active_player_cap`, `challenge_not_found`, `channel_full`,
`config_version_conflict`, `config_version_not_found`, `cooldown` (+`retryAt`), `duel_not_found`,
Expand Down Expand Up @@ -485,7 +485,8 @@ Viewer sprites (server/sprites.ts):
| initialElo | 1000 | 0 to 10000 |
| eloK | 24 | 1 to 100 |
| checkinPoints | 1 | 0 to 3 (upgrade points per check-in) |
| streakBonus | true | boolean (+1 point at a streak of 3, 7, 14 and 30 streams) |
| streakBonus | true | boolean (+1 point at a streak of 3, 7, 14 and 30 streams, +2 at 60, +3 at 100, +5 at 200, +10 at 365) |
| maxStatLevel | 16 | 1 to 200 (the highest level of each upgrade stat that counts in this channel's duels) |
| winDollars | 5 | 0 to 100 (PixFray dollars for a win) |
| lossDollars | 3 | 0 to 100 (dollars for a loss) |
| giveEnabled | true | boolean (`!pay` on or off) |
Expand Down
8 changes: 6 additions & 2 deletions docs/DUELS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ The duel is rolled at once, the moment the challenge is accepted.

## Upgrades, hats and check-ins

- Each win earns an upgrade point (power, guard, luck) and unlocks hats.
- `!checkin`, once per live stream, adds a point too, with streak bonuses.
- Each win earns an upgrade point (power, guard, luck) and unlocks hats. Points have no cap.
- Levels 1-8 of a stat cost 1 point each; the cost doubles every 8 levels after that, and each level
adds less as the stat nears its ceiling. Mods set the highest level that counts on their channel
(16 by default); levels above it stay saved.
- `!checkin`, once per live stream, adds a point too, with streak bonuses: +1 at 3, 7, 14 and 30
streams, +2 at 60, +3 at 100, +5 at 200 and +10 at 365.
- `!pet` shows a fighter's pet.

## PixFray dollars
Expand Down
2 changes: 2 additions & 0 deletions docs/STREAMER_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ once you allowed the bot or made it a moderator.
it, click **Connect mod access** and approve the permission while signed in to Twitch as yourself.

Moderators can edit chat commands, use Clear arena, change the balance and review viewer sprites.
The Rules tab's Check-ins group has **Highest stat level that counts** (16 by default): fighters' upgrade levels above it
stay saved but don't count in your channel's duels.
Only you (or the site owner) can click **Reset all ranks**, **Turn PixFray off** or move chat from
another site.

Expand Down
7 changes: 4 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ <h1 id="hero-title" data-channel>Your fighter in %SITE_CHANNEL%'s chat</h1>
</fieldset>
<fieldset id="upgrades">
<legend>Upgrades <span class="muted small" id="points-note"></span></legend>
<p class="muted small" id="upgrades-help">Each win and each stream check-in (!checkin in chat) gives a point, up to 20. Move points between stats whenever you're not in a duel.</p>
<p class="muted small" id="upgrades-help">Each win and each stream check-in (!checkin in chat) gives a point. Move points between stats whenever you're not in a duel.</p>
<div class="upgrade-list" id="upgrade-list"></div>
<div class="filter-buttons" id="upgrade-presets" role="group" aria-label="Suggested builds" hidden></div>
</fieldset>
</div>

Expand Down Expand Up @@ -180,7 +181,7 @@ <h1 id="hero-title" data-channel>Your fighter in %SITE_CHANNEL%'s chat</h1>
<div class="form-stack" role="tabpanel" id="panel-pets" aria-labelledby="tab-pets" hidden>
<fieldset id="pets">
<legend>Pet <span class="muted small" id="pet-note"></span></legend>
<p class="muted small">A pet follows your fighter on stream and adds to its stats, on top of the upgrade limit. Buy pets with the PixFray dollars you earn in duels. You keep every pet you buy and bring one at a time.</p>
<p class="muted small">A pet follows your fighter on stream and adds to its stats, on top of the channel's level limit. Buy pets with the PixFray dollars you earn in duels. You keep every pet you buy and bring one at a time.</p>
<div class="char-grid item-grid" id="pet-list" role="radiogroup" aria-label="Pet"></div>
<p class="status" id="pet-status" role="status"></p>
</fieldset>
Expand Down Expand Up @@ -223,7 +224,7 @@ <h3>Commands</h3>
<tr><td><code>!fight</code></td><td>Accepts a challenge sent to you. The duel is rolled right away and plays out on stream.</td></tr>
<tr><td><code>!decline</code></td><td>Turns a challenge down.</td></tr>
<tr><td><code>!rematch</code></td><td>Challenges the last viewer you fought. If they type <code>!rematch</code> too, the duel starts. Rematches open 30 s after a duel.</td></tr>
<tr><td><code>!checkin</code></td><td>Once per stream, while it's live: <span id="cmd-checkin">+1 upgrade point</span>. Check in on streams in a row for +1 more at 3, 7, 14 and 30. One missed stream a week doesn't break the streak.</td></tr>
<tr><td><code>!checkin</code></td><td>Once per stream, while it's live: <span id="cmd-checkin">+1 upgrade point</span>. Check in on streams in a row for +1 more at 3, 7, 14 and 30, +2 at 60, +3 at 100, +5 at 200 and +10 at 365. One missed stream a week doesn't break the streak.</td></tr>
<tr><td><code>!wallet</code></td><td>Posts your PixFray dollars, upgrade points and streak. Each finished duel pays <span id="cmd-dollars">$5 for a win and $3 for a loss</span>.</td></tr>
<tr><td><code>!pay @viewer 10</code></td><td id="cmd-give">Gives dollars to another fighter while the stream is live: up to $100 per stream, after your first 5 duels.</td></tr>
<tr><td><code>!pet</code></td><td>Posts the pet you bring and its boost. Add <code>@viewer</code> to see someone else's.</td></tr>
Expand Down
2 changes: 1 addition & 1 deletion public/dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ fieldset .status:empty {
}
.upgrade {
display: grid;
grid-template-columns: minmax(0, 1fr) auto 4.5em auto;
grid-template-columns: minmax(0, 1fr) auto 6.5em auto;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
Expand Down
Loading
Loading