Public Access
feat(rewards): add completed, background and banner effect items
This commit is contained in:
1 parent
349fa40272
commit
a12ed20705
7 files changed
+42
-8
No files matched your search
@@ -63,7 +63,8 @@ To Be Played is split into three repositories:
|
||||
snapshots, once per user, kind and game (`ref`). Rules and numbers: `RewardRules`; a Steam finish needs playtime and one
|
||||
proof of fresh play (ending achievement or playtime around the finish, or half of the IGDB time played since the finish; old history pays nothing). The month bonus needs a finished game in that month. Games added by hand pay 15 for
|
||||
a finish, at most 3 per ISO week. The settle takes a per-user advisory lock. V26 also pays a one-time starter grant
|
||||
for finishes from before the status log. Shop: a whitelist of 8 looks (`ShopItem`); a `BUY` row proves ownership,
|
||||
for finishes from before the status log. Shop: a whitelist of 28 looks (`ShopItem`) in seven slots, including animated completed effects, page backgrounds and banner
|
||||
backgrounds (V27 widens `reward_equipped.slot`); a `BUY` row proves ownership,
|
||||
`reward_equipped` holds what is worn. A platform admin owns every item (the role of the login token, or the `role`
|
||||
claim of the profile ticket for visitors).
|
||||
- **Wishlist**: synced from Steam with store price and discount.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package core_service.modules.rewards.domain;
|
||||
|
||||
public enum LookSlot {
|
||||
AVATAR_BORDER, BADGE, ACCENT, BANNER_FRAME
|
||||
AVATAR_BORDER, BADGE, ACCENT, BANNER_FRAME, COMPLETED_EFFECT, PAGE_BACKGROUND, BANNER_BACKGROUND
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The whitelist of looks. The frontend maps a code to its visuals; nothing here is ever uploaded CSS.
|
||||
* {@code FX_} effects show on the cover of a COMPLETED game and on its details page, {@code BG_} on the page
|
||||
* background, {@code BN_} on the dashboard and profile banner.
|
||||
*/
|
||||
public enum ShopItem {
|
||||
BORDER_GOLD(LookSlot.AVATAR_BORDER, 150),
|
||||
@@ -15,7 +17,27 @@ public enum ShopItem {
|
||||
ACCENT_GOLD(LookSlot.ACCENT, 200),
|
||||
ACCENT_TEAL(LookSlot.ACCENT, 200),
|
||||
FRAME_GOLD(LookSlot.BANNER_FRAME, 150),
|
||||
FRAME_NEON(LookSlot.BANNER_FRAME, 200);
|
||||
FRAME_NEON(LookSlot.BANNER_FRAME, 200),
|
||||
FX_GOLD_SHINE(LookSlot.COMPLETED_EFFECT, 300),
|
||||
FX_NEON_PULSE(LookSlot.COMPLETED_EFFECT, 300),
|
||||
FX_HOLO(LookSlot.COMPLETED_EFFECT, 350),
|
||||
FX_EMBER(LookSlot.COMPLETED_EFFECT, 300),
|
||||
BG_TRIANGLES(LookSlot.PAGE_BACKGROUND, 400),
|
||||
BG_STARFIELD(LookSlot.PAGE_BACKGROUND, 400),
|
||||
BG_PIXELS(LookSlot.PAGE_BACKGROUND, 400),
|
||||
BG_AURORA(LookSlot.PAGE_BACKGROUND, 450),
|
||||
BG_MESH(LookSlot.PAGE_BACKGROUND, 450),
|
||||
BG_GRID(LookSlot.PAGE_BACKGROUND, 450),
|
||||
BG_WAVES(LookSlot.PAGE_BACKGROUND, 400),
|
||||
BG_BOKEH(LookSlot.PAGE_BACKGROUND, 400),
|
||||
BN_TRIANGLES(LookSlot.BANNER_BACKGROUND, 300),
|
||||
BN_STARFIELD(LookSlot.BANNER_BACKGROUND, 300),
|
||||
BN_PIXELS(LookSlot.BANNER_BACKGROUND, 300),
|
||||
BN_AURORA(LookSlot.BANNER_BACKGROUND, 300),
|
||||
BN_MESH(LookSlot.BANNER_BACKGROUND, 300),
|
||||
BN_GRID(LookSlot.BANNER_BACKGROUND, 300),
|
||||
BN_WAVES(LookSlot.BANNER_BACKGROUND, 300),
|
||||
BN_BOKEH(LookSlot.BANNER_BACKGROUND, 300);
|
||||
|
||||
private final LookSlot slot;
|
||||
private final int price;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- BANNER_BACKGROUND is 17 characters.
|
||||
ALTER TABLE reward_equipped ALTER COLUMN slot TYPE VARCHAR(32);
|
||||
@@ -1,6 +1,7 @@
|
||||
package core_service.modules.rewards.api;
|
||||
|
||||
import core_service.configuration.security.ClientHeaderFilter;
|
||||
import core_service.modules.rewards.domain.ShopItem;
|
||||
import core_service.modules.rewards.service.RewardService;
|
||||
import core_service.shared.security.Role;
|
||||
import core_service.shared.test.AbstractIntegrationTest;
|
||||
@@ -81,7 +82,7 @@ class RewardControllerTest extends AbstractIntegrationTest {
|
||||
.andExpect(jsonPath("$.balance").value(30))
|
||||
.andExpect(jsonPath("$.history[0].kind").value("FINISHED"))
|
||||
.andExpect(jsonPath("$.history[0].gameName").value("Game " + GAME))
|
||||
.andExpect(jsonPath("$.catalog.length()").value(8))
|
||||
.andExpect(jsonPath("$.catalog.length()").value(ShopItem.values().length))
|
||||
.andExpect(jsonPath("$.owned").isEmpty());
|
||||
|
||||
setStatus(GAME, "BACKLOG");
|
||||
@@ -212,7 +213,7 @@ class RewardControllerTest extends AbstractIntegrationTest {
|
||||
@Test
|
||||
void admin_OwnsEveryItem_NeverBuysAndFallsBackToDefaultAsAUser() throws Exception {
|
||||
mockMvc.perform(asAdmin(get("/api/rewards/wallet")))
|
||||
.andExpect(jsonPath("$.owned.length()").value(8));
|
||||
.andExpect(jsonPath("$.owned.length()").value(ShopItem.values().length));
|
||||
mockMvc.perform(asAdmin(post("/api/rewards/purchases")).header(ClientHeaderFilter.CLIENT_HEADER, "test")
|
||||
.contentType(MediaType.APPLICATION_JSON).content("{\"item\":\"FRAME_GOLD\"}"))
|
||||
.andExpect(status().isConflict()).andExpect(jsonPath("$.code").value("ALREADY_OWNED"));
|
||||
|
||||
@@ -25,9 +25,10 @@ class LooksTest extends AbstractUnitTest {
|
||||
@Test
|
||||
void wearable_DropsLooksThatAreNotOwnedOrNotInTheCatalog() {
|
||||
Map<LookSlot, String> equipped = Map.of(LookSlot.AVATAR_BORDER, "BORDER_GOLD", LookSlot.BADGE, "BADGE_PILE_SLAYER",
|
||||
LookSlot.ACCENT, "ACCENT_NEON_PINK");
|
||||
LookSlot.ACCENT, "ACCENT_NEON_PINK", LookSlot.BANNER_BACKGROUND, "BN_GRID");
|
||||
|
||||
assertThat(Looks.wearable(equipped, Set.of("BORDER_GOLD", "ACCENT_NEON_PINK")))
|
||||
.containsOnly(Map.entry(LookSlot.AVATAR_BORDER, "BORDER_GOLD"));
|
||||
assertThat(Looks.wearable(equipped, Set.of("BORDER_GOLD", "ACCENT_NEON_PINK", "BN_GRID")))
|
||||
.containsOnly(Map.entry(LookSlot.AVATAR_BORDER, "BORDER_GOLD"),
|
||||
Map.entry(LookSlot.BANNER_BACKGROUND, "BN_GRID"));
|
||||
}
|
||||
}
|
||||
@@ -99,4 +99,11 @@ class RewardLedgerRepositoryTest extends AbstractPersistenceTest {
|
||||
|
||||
assertThat(repository.equipped(USER)).containsOnlyKeys(LookSlot.ACCENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equip_TheLongestSlotNameFits() {
|
||||
repository.equip(USER, LookSlot.BANNER_BACKGROUND, "BN_GRID");
|
||||
|
||||
assertThat(repository.equipped(USER)).containsOnly(Map.entry(LookSlot.BANNER_BACKGROUND, "BN_GRID"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user