From bf5ed71c2590dde05fde70ae3b6cc9d33238c044 Mon Sep 17 00:00:00 2001 From: notV4l Date: Fri, 24 Nov 2023 15:33:40 +0100 Subject: [PATCH] reintroduce turn cap & test mode --- src/models/game.cairo | 4 -- src/models/player.cairo | 3 +- src/systems/decide.cairo | 1 + src/systems/devtools.cairo | 1 + src/systems/lobby.cairo | 7 ++-- src/systems/travel.cairo | 17 +++++++++ src/tests/create.cairo | 1 - src/utils/settings.cairo | 7 +++- web/manifest.json | 38 ++++++++++++------- web/src/components/Header.tsx | 2 +- web/src/dojo/generated/contractModels.ts | 5 ++- web/src/dojo/hooks/useSystems.ts | 21 ++++++++++ web/src/dojo/queries/usePlayerEntity.tsx | 5 +++ web/src/generated/graphql.ts | 29 +++++++------- web/src/graphql/components.graphql | 2 +- web/src/graphql/entities.graphql | 1 + .../pages/[gameId]/[locationSlug]/index.tsx | 8 +++- web/src/pages/[gameId]/end.tsx | 6 ++- web/src/pages/create/new.tsx | 6 +-- 19 files changed, 116 insertions(+), 48 deletions(-) diff --git a/src/models/game.cairo b/src/models/game.cairo index 05ce70796..9564c84a8 100644 --- a/src/models/game.cairo +++ b/src/models/game.cairo @@ -9,7 +9,6 @@ struct Game { max_players: usize, num_players: usize, max_turns: usize, - is_finished: bool, creator: ContractAddress, } @@ -29,9 +28,6 @@ impl GameImpl of GameTrait { if info.block_timestamp < self.start_time { return false; } - if self.is_finished { - return false; - } true } diff --git a/src/models/player.cairo b/src/models/player.cairo index b145ec052..3aada2099 100644 --- a/src/models/player.cairo +++ b/src/models/player.cairo @@ -30,7 +30,8 @@ struct Player { transport: usize, speed: usize, wanted: u8, - leaderboard_version: u32 + leaderboard_version: u32, + game_over: bool, } #[generate_trait] diff --git a/src/systems/decide.cairo b/src/systems/decide.cairo index 8e2e85735..70405d26d 100644 --- a/src/systems/decide.cairo +++ b/src/systems/decide.cairo @@ -219,6 +219,7 @@ mod decide { if health_loss >= player.health { player.health = 0; + player.game_over = true; outcome = Outcome::Died; let leaderboard_manager = LeaderboardManagerTrait::new(self.world()); diff --git a/src/systems/devtools.cairo b/src/systems/devtools.cairo index 8be62335c..dacbfe56c 100644 --- a/src/systems/devtools.cairo +++ b/src/systems/devtools.cairo @@ -72,6 +72,7 @@ mod devtools { transport: 42, speed: 42, leaderboard_version, + game_over: true, }; set!(self.world(), (player)); diff --git a/src/systems/lobby.cairo b/src/systems/lobby.cairo index b75bc35e2..fdcf7881e 100644 --- a/src/systems/lobby.cairo +++ b/src/systems/lobby.cairo @@ -103,7 +103,8 @@ mod lobby { defense: player_settings.defense, transport: player_settings.transport, speed: player_settings.speed, - leaderboard_version + leaderboard_version, + game_over: false, }; let game = Game { @@ -113,7 +114,6 @@ mod lobby { max_players: game_settings.max_players, num_players: 1, // caller auto joins max_turns: game_settings.max_turns, - is_finished: false, creator: caller, }; @@ -153,7 +153,8 @@ mod lobby { } fn assert_valid_chain(game_mode: GameMode) { - assert(game_mode == GameMode::Unlimited, 'invalid game_mode'); + // assert(game_mode == GameMode::Unlimited, 'invalid game_mode'); + //if game_mode == GameMode::Test { // let chain_id = get_tx_info().unbox().chain_id; // assert(chain_id != 'KATANA', 'wrong chain_id'); diff --git a/src/systems/travel.cairo b/src/systems/travel.cairo index 942cc5b02..5e97ac81d 100644 --- a/src/systems/travel.cairo +++ b/src/systems/travel.cairo @@ -6,6 +6,7 @@ use rollyourown::models::location::LocationEnum; #[starknet::interface] trait ITravel { fn travel(self: @TContractState, game_id: u32, next_location_id: LocationEnum) -> bool; + fn end_game(self: @TContractState, game_id: u32); } #[dojo::contract] @@ -28,6 +29,7 @@ mod travel { use rollyourown::utils::risk::{RiskTrait, RiskImpl}; use rollyourown::utils::math::{MathTrait, MathImplU8}; use rollyourown::utils::random::{Random, RandomImpl}; + use rollyourown::utils::leaderboard::{LeaderboardManager, LeaderboardManagerTrait}; use super::ITravel; use super::on_turn_end; @@ -152,6 +154,21 @@ mod travel { false } + + fn end_game(self: @ContractState, game_id: u32) { + let world = self.world(); + let player_id = get_caller_address(); + + let mut player: Player = get!(world, (game_id, player_id).into(), Player); + assert(player.game_over == false, 'already game_over'); + + player.game_over = true; + set!(world, (player)); + + let leaderboard_manager = LeaderboardManagerTrait::new(self.world()); + leaderboard_manager.on_game_end(player.cash); + } + } } diff --git a/src/tests/create.cairo b/src/tests/create.cairo index 2f1579bbd..c638b374e 100644 --- a/src/tests/create.cairo +++ b/src/tests/create.cairo @@ -69,7 +69,6 @@ fn spawn_game() -> (ContractAddress, u32, felt252) { assert(game.start_time == START_TIME, 'start time mismatch'); assert(game.max_players == MAX_PLAYERS, 'max players mismatch'); assert(game.max_turns == MAX_TURNS, 'max turns mismatch'); - assert(game.is_finished == false, 'game is finished mismatch'); (world.contract_address, game_id, player_id) } diff --git a/src/utils/settings.cairo b/src/utils/settings.cairo index 6f58a7303..913a20e2b 100644 --- a/src/utils/settings.cairo +++ b/src/utils/settings.cairo @@ -117,7 +117,12 @@ trait EncounterSettingsTrait { impl GameSettingsImpl of SettingsTrait { fn get(game_mode: GameMode) -> GameSettings { - let game_settings = GameSettings { max_players: 1, max_turns: 0 }; + let mut game_settings = GameSettings { max_players: 1, max_turns: 42 }; + + if game_mode == GameMode::Test { + game_settings.max_turns = 5; + } + game_settings } } diff --git a/web/manifest.json b/web/manifest.json index 0aa1b4731..5fc77aa47 100644 --- a/web/manifest.json +++ b/web/manifest.json @@ -909,7 +909,7 @@ { "name": "decide", "address": "0x59fed3332e583eb4ff6c0ff52a45c08b823025f2bc2d1b148e14a3e089ce271", - "class_hash": "0xbc1cd02cccc564f4a34c8ee1d3dd20829b6b9e6a9db44fdb0b691ecc71976f", + "class_hash": "0x4f0237ac51e1fbf4e312ad9034fc7f2efe9d100233894cf336b8e62f8f86025", "abi": [ { "type": "impl", @@ -1247,7 +1247,7 @@ { "name": "devtools", "address": "0x3e76834b68cd7aa2ec3da8ad4022bace3d9a0ff9f5568e5c4e5f8435474f2b5", - "class_hash": "0x4a5d1613cc8cfb562d3fc0d0552f464f8313bbe1aff617a4147573962bbc9da", + "class_hash": "0x31ea04e15d7b101247898b533edbcd9c224e42118ed01b920874e0b0dfb7d8e", "abi": [ { "type": "impl", @@ -1389,7 +1389,7 @@ { "name": "lobby", "address": "0x4c03b718b8cda385d9ba4cf9eedec6588fffe053fbc9a5de3840226c981f00d", - "class_hash": "0x284167a7b42a1a01b580f04b8e067cc69d3643fdbd38f05844933f9ba9b3f86", + "class_hash": "0x4225ba28a49bf788d2eba0757c1a12dacefd2b0ea1c3e4ac26e8e9e634f205b", "abi": [ { "type": "impl", @@ -1762,7 +1762,7 @@ { "name": "shop", "address": "0x49666a158f5d130c9efebd24aaa99b0c52796439fab687cef3d089a35d97e31", - "class_hash": "0xd93571e6279c44d980a7bb751e89519ae54903122e8eded0e4def7f728010e", + "class_hash": "0xeff9452fe41f5dddf8b9e81b999fad03bac852352797ec94aaf34c09b8549d", "abi": [ { "type": "impl", @@ -2099,7 +2099,7 @@ { "name": "trade", "address": "0x5decbe746e8199f7162d9cfbf3cb84fff20712e4fec76c729a4e0a8725fbc4", - "class_hash": "0x55c5b19899758fd0385948cf61728c77bb7511fdf1f88346219dbf68e1309a7", + "class_hash": "0x26e9a1222efc369cf91f0026c81f39fb2b2ad80f72a3eaf8728d0f9efd4a7bb", "abi": [ { "type": "impl", @@ -2417,7 +2417,7 @@ { "name": "travel", "address": "0x2ec3dd5ae820ac90b7a95af30b9d62c2b13d3f26c948750d9d3f38a1a7a9fbd", - "class_hash": "0x350787165207d8dc9acb4e96726bca3b30f830fa4812dcc21427e13802033b3", + "class_hash": "0x6518aa200bbb25603ef9e047fb131f2fde40411e379c10f34ab501f5e8bca41", "abi": [ { "type": "impl", @@ -2527,6 +2527,18 @@ } ], "state_mutability": "view" + }, + { + "type": "function", + "name": "end_game", + "inputs": [ + { + "name": "game_id", + "type": "core::integer::u32" + } + ], + "outputs": [], + "state_mutability": "view" } ] }, @@ -3214,18 +3226,13 @@ "type": "usize", "key": false }, - { - "name": "is_finished", - "type": "bool", - "key": false - }, { "name": "creator", "type": "ContractAddress", "key": false } ], - "class_hash": "0x23b1befabb6193697bfeef78eae927b62224e1924ee405f9b8e60052a7bfe3a", + "class_hash": "0x5a281cfdf88fa6fd701345c663480c71f9bbe7462902b54a7245e09f25ea2fa", "abi": [ { "type": "function", @@ -4056,9 +4063,14 @@ "name": "leaderboard_version", "type": "u32", "key": false + }, + { + "name": "game_over", + "type": "bool", + "key": false } ], - "class_hash": "0x7f44c9585e56584edbb490fbc7a7cc6636f078d66086b1f776358eb518baa98", + "class_hash": "0x2ebd2ef65f9a3a8f9da19a1353a1904d846a5213ee63d7340402f955efaa84d", "abi": [ { "type": "function", diff --git a/web/src/components/Header.tsx b/web/src/components/Header.tsx index 361b46d86..6e6fefb37 100644 --- a/web/src/components/Header.tsx +++ b/web/src/components/Header.tsx @@ -55,7 +55,7 @@ const Header = ({ back }: HeaderProps) => { return ( - {playerEntity && playerEntity.health > 0 && ( + {playerEntity && playerEntity.health > 0 && !playerEntity.gameOver && ( Promise; travel: (gameId: string, locationId: Location) => Promise; + endGame: (gameId: string) => Promise; // join: (gameId: string) => Promise; buy: ( gameId: string, @@ -203,6 +204,25 @@ export const useSystems = (): SystemsInterface => { [executeAndReceipt], ); + const endGame = useCallback( + async (gameId: string) => { + const { hash, events, parsedEvents } = await executeAndReceipt( + "travel", + "end_game", + [gameId], + ); + + return { + hash, + event: [], + events: [], + }; + }, + [executeAndReceipt], + ); + + + const buy = useCallback( async ( gameId: string, @@ -359,6 +379,7 @@ export const useSystems = (): SystemsInterface => { createGame, // join, travel, + endGame, buy, sell, //setName, diff --git a/web/src/dojo/queries/usePlayerEntity.tsx b/web/src/dojo/queries/usePlayerEntity.tsx index a483f381b..a698c3040 100644 --- a/web/src/dojo/queries/usePlayerEntity.tsx +++ b/web/src/dojo/queries/usePlayerEntity.tsx @@ -39,6 +39,7 @@ export class PlayerEntity { speed: number; wanted: number; + gameOver: boolean; constructor(player: Player, drugs: Drug[], items: ShopItem[], encounters: Encounter[]) { this.name = shortString.decodeShortString(player.name); @@ -61,10 +62,13 @@ export class PlayerEntity { this.speed = player.speed; this.wanted = player.wanted; + this.gameOver = player.game_over; + this.drugs = drugs; this.items = items; this.encounters = encounters; + } update(player: Player) { @@ -75,6 +79,7 @@ export class PlayerEntity { this.locationId = player.location_id === "Home" ? undefined : player.location_id; this.status = player.status; this.wanted = player.wanted; + this.gameOver = player.game_over; return this; } diff --git a/web/src/generated/graphql.ts b/web/src/generated/graphql.ts index 04b5bfedd..468f01923 100644 --- a/web/src/generated/graphql.ts +++ b/web/src/generated/graphql.ts @@ -176,7 +176,6 @@ export type Game = { entity?: Maybe; game_id?: Maybe; game_mode?: Maybe; - is_finished?: Maybe; max_players?: Maybe; max_turns?: Maybe; num_players?: Maybe; @@ -205,7 +204,6 @@ export enum GameOrderField { Creator = 'CREATOR', GameId = 'GAME_ID', GameMode = 'GAME_MODE', - IsFinished = 'IS_FINISHED', MaxPlayers = 'MAX_PLAYERS', MaxTurns = 'MAX_TURNS', NumPlayers = 'NUM_PLAYERS', @@ -228,13 +226,6 @@ export type GameWhereInput = { game_idLTE?: InputMaybe; game_idNEQ?: InputMaybe; game_mode?: InputMaybe; - is_finished?: InputMaybe; - is_finishedEQ?: InputMaybe; - is_finishedGT?: InputMaybe; - is_finishedGTE?: InputMaybe; - is_finishedLT?: InputMaybe; - is_finishedLTE?: InputMaybe; - is_finishedNEQ?: InputMaybe; max_players?: InputMaybe; max_playersEQ?: InputMaybe; max_playersGT?: InputMaybe; @@ -370,7 +361,7 @@ export type LeaderboardOrder = { export enum LeaderboardOrderField { HighScore = 'HIGH_SCORE', - HighScoreTimestamp = 'next_version_timestamp', + NextVersionTimestamp = 'NEXT_VERSION_TIMESTAMP', Version = 'VERSION' } @@ -476,6 +467,7 @@ export type Player = { drug_count?: Maybe; entity?: Maybe; game_id?: Maybe; + game_over?: Maybe; health?: Maybe; leaderboard_version?: Maybe; location_id?: Maybe; @@ -516,6 +508,7 @@ export enum PlayerOrderField { Defense = 'DEFENSE', DrugCount = 'DRUG_COUNT', GameId = 'GAME_ID', + GameOver = 'GAME_OVER', Health = 'HEALTH', LeaderboardVersion = 'LEADERBOARD_VERSION', LocationId = 'LOCATION_ID', @@ -574,6 +567,13 @@ export type PlayerWhereInput = { game_idLT?: InputMaybe; game_idLTE?: InputMaybe; game_idNEQ?: InputMaybe; + game_over?: InputMaybe; + game_overEQ?: InputMaybe; + game_overGT?: InputMaybe; + game_overGTE?: InputMaybe; + game_overLT?: InputMaybe; + game_overLTE?: InputMaybe; + game_overNEQ?: InputMaybe; health?: InputMaybe; healthEQ?: InputMaybe; healthGT?: InputMaybe; @@ -1079,7 +1079,7 @@ export type LeaderboardMetasQueryVariables = Exact<{ export type LeaderboardMetasQuery = { __typename?: 'World__Query', leaderboardModels?: { __typename?: 'LeaderboardConnection', edges?: Array<{ __typename?: 'LeaderboardEdge', node?: { __typename?: 'Leaderboard', version?: any | null, high_score?: any | null, next_version_timestamp?: any | null } | null } | null> | null } | null }; -export type PlayerPropsFragment = { __typename?: 'Player', name?: any | null, avatar_id?: any | null, cash?: any | null, status?: any | null, location_id?: any | null, drug_count?: any | null, health?: any | null, turn?: any | null, max_turns?: any | null, max_items?: any | null, attack?: any | null, defense?: any | null, transport?: any | null, speed?: any | null, wanted?: any | null }; +export type PlayerPropsFragment = { __typename?: 'Player', name?: any | null, avatar_id?: any | null, cash?: any | null, status?: any | null, location_id?: any | null, drug_count?: any | null, health?: any | null, turn?: any | null, max_turns?: any | null, max_items?: any | null, attack?: any | null, defense?: any | null, transport?: any | null, speed?: any | null, wanted?: any | null, game_over?: any | null }; export type PlayerEntityQueryVariables = Exact<{ gameId: Scalars['String']; @@ -1087,14 +1087,14 @@ export type PlayerEntityQueryVariables = Exact<{ }>; -export type PlayerEntityQuery = { __typename?: 'World__Query', entities?: { __typename?: 'World__EntityConnection', total_count: number, edges?: Array<{ __typename?: 'World__EntityEdge', node?: { __typename?: 'World__Entity', id?: string | null, models?: Array<{ __typename: 'Drug', drug_id?: any | null, quantity?: any | null } | { __typename: 'Encounter', encounter_id?: any | null, level?: any | null, health?: any | null, payout?: any | null } | { __typename: 'Game' } | { __typename: 'Item', item_id?: any | null, level?: any | null, name?: any | null, value?: any | null } | { __typename: 'Leaderboard' } | { __typename: 'Market' } | { __typename: 'Player', name?: any | null, avatar_id?: any | null, cash?: any | null, status?: any | null, location_id?: any | null, drug_count?: any | null, health?: any | null, turn?: any | null, max_turns?: any | null, max_items?: any | null, attack?: any | null, defense?: any | null, transport?: any | null, speed?: any | null, wanted?: any | null } | { __typename: 'RyoMeta' } | null> | null } | null } | null> | null } | null }; +export type PlayerEntityQuery = { __typename?: 'World__Query', entities?: { __typename?: 'World__EntityConnection', total_count: number, edges?: Array<{ __typename?: 'World__EntityEdge', node?: { __typename?: 'World__Entity', id?: string | null, models?: Array<{ __typename: 'Drug', drug_id?: any | null, quantity?: any | null } | { __typename: 'Encounter', encounter_id?: any | null, level?: any | null, health?: any | null, payout?: any | null } | { __typename: 'Game' } | { __typename: 'Item', item_id?: any | null, level?: any | null, name?: any | null, value?: any | null } | { __typename: 'Leaderboard' } | { __typename: 'Market' } | { __typename: 'Player', name?: any | null, avatar_id?: any | null, cash?: any | null, status?: any | null, location_id?: any | null, drug_count?: any | null, health?: any | null, turn?: any | null, max_turns?: any | null, max_items?: any | null, attack?: any | null, defense?: any | null, transport?: any | null, speed?: any | null, wanted?: any | null, game_over?: any | null } | { __typename: 'RyoMeta' } | null> | null } | null } | null> | null } | null }; export type PlayerEntitySubscriptionSubscriptionVariables = Exact<{ id?: InputMaybe; }>; -export type PlayerEntitySubscriptionSubscription = { __typename?: 'World__Subscription', entityUpdated: { __typename?: 'World__Entity', id?: string | null, keys?: Array | null, model_names?: string | null, models?: Array<{ __typename: 'Drug' } | { __typename: 'Encounter' } | { __typename: 'Game' } | { __typename: 'Item' } | { __typename: 'Leaderboard' } | { __typename: 'Market' } | { __typename: 'Player', name?: any | null, avatar_id?: any | null, cash?: any | null, status?: any | null, location_id?: any | null, drug_count?: any | null, health?: any | null, turn?: any | null, max_turns?: any | null, max_items?: any | null, attack?: any | null, defense?: any | null, transport?: any | null, speed?: any | null, wanted?: any | null } | { __typename: 'RyoMeta' } | null> | null } }; +export type PlayerEntitySubscriptionSubscription = { __typename?: 'World__Subscription', entityUpdated: { __typename?: 'World__Entity', id?: string | null, keys?: Array | null, model_names?: string | null, models?: Array<{ __typename: 'Drug' } | { __typename: 'Encounter' } | { __typename: 'Game' } | { __typename: 'Item' } | { __typename: 'Leaderboard' } | { __typename: 'Market' } | { __typename: 'Player', name?: any | null, avatar_id?: any | null, cash?: any | null, status?: any | null, location_id?: any | null, drug_count?: any | null, health?: any | null, turn?: any | null, max_turns?: any | null, max_items?: any | null, attack?: any | null, defense?: any | null, transport?: any | null, speed?: any | null, wanted?: any | null, game_over?: any | null } | { __typename: 'RyoMeta' } | null> | null } }; export type PlayerEntityRelatedDataSubscriptionSubscriptionVariables = Exact<{ id?: InputMaybe; @@ -1136,12 +1136,13 @@ export const PlayerPropsFragmentDoc = ` transport speed wanted + game_over } `; export const GlobalScoresDocument = ` query GlobalScores($version: u32, $limit: Int, $cursor: Cursor) { playerModels( - where: {health: 0, leaderboard_version: $version} + where: {game_over: true, leaderboard_version: $version} order: {direction: DESC, field: CASH} first: $limit after: $cursor diff --git a/web/src/graphql/components.graphql b/web/src/graphql/components.graphql index 6e9b7d827..2bb95cc39 100644 --- a/web/src/graphql/components.graphql +++ b/web/src/graphql/components.graphql @@ -1,6 +1,6 @@ query GlobalScores($version: u32, $limit: Int, $cursor: Cursor) { playerModels( - where: { health: 0, leaderboard_version: $version } + where: { game_over: true, leaderboard_version: $version } order: { direction: DESC, field: CASH } first: $limit after: $cursor diff --git a/web/src/graphql/entities.graphql b/web/src/graphql/entities.graphql index b52f1e64b..9554ed88c 100644 --- a/web/src/graphql/entities.graphql +++ b/web/src/graphql/entities.graphql @@ -14,6 +14,7 @@ fragment PlayerProps on Player { transport speed wanted + game_over } query PlayerEntity($gameId: String!, $playerId: String!) { diff --git a/web/src/pages/[gameId]/[locationSlug]/index.tsx b/web/src/pages/[gameId]/[locationSlug]/index.tsx index d754df15d..564edc361 100644 --- a/web/src/pages/[gameId]/[locationSlug]/index.tsx +++ b/web/src/pages/[gameId]/[locationSlug]/index.tsx @@ -26,6 +26,7 @@ import { shortString } from "starknet"; import Button from "@/components/Button"; import { getDrugById, getLocationById, getLocationBySlug, sortDrugMarkets } from "@/dojo/helpers"; import { motion } from "framer-motion"; +import { useSystems } from "@/dojo/hooks/useSystems"; export default function Location() { const router = useRouter(); @@ -38,6 +39,8 @@ export default function Location() { locationId, }); + const { endGame, isPending } = useSystems(); + const { playerEntity } = playerEntityStore; const [isLastDay, setIsLastDay] = useState(false); @@ -74,9 +77,10 @@ export default function Location() { - {/* */} + } >