From 7f23206ec7ce79847699ea2fa92406927573c696 Mon Sep 17 00:00:00 2001 From: broody Date: Sat, 9 Sep 2023 10:55:50 -0700 Subject: [PATCH] refactor --- scripts/default_auth.sh | 5 + src/components/player.cairo | 4 +- src/components/risks.cairo | 8 +- src/constants.cairo | 25 +-- src/systems/create.cairo | 59 +++-- src/systems/decide.cairo | 121 ++++++++--- src/systems/join.cairo | 58 ++--- src/systems/trade.cairo | 24 ++- src/systems/travel.cairo | 9 +- src/tests.cairo | 3 +- src/tests/trade.cairo | 133 +++--------- web/src/components/Header.tsx | 31 +-- web/src/components/Inventory.tsx | 8 +- web/src/components/Layout.tsx | 42 ++-- web/src/components/Leaderboard.tsx | 16 +- web/src/components/MakeItRain.tsx | 1 - web/src/components/map/Callout.tsx | 16 +- web/src/components/map/HitBox.tsx | 16 +- web/src/components/map/Map.tsx | 20 +- web/src/components/map/Markers.tsx | 16 +- web/src/components/map/Outline.tsx | 18 +- .../dojo/components/useGlobalScores.tsx | 0 .../dojo/entities/useGameEntity.tsx | 0 .../dojo/entities/useLocationEntity.tsx | 0 .../dojo/entities/usePlayerEntity.tsx | 2 + web/src/{utils/event.ts => dojo/events.ts} | 12 +- web/src/dojo/helpers.ts | 172 +++++++++++++++ web/src/{hooks => }/dojo/index.tsx | 2 +- .../{hooks => }/dojo/systems/useSystems.tsx | 6 +- web/src/dojo/types.ts | 51 +++++ web/src/generated/graphql.ts | 18 ++ web/src/graphql/entities.graphql | 1 + web/src/hooks/burner.tsx | 4 +- web/src/hooks/state.tsx | 57 ++--- web/src/hooks/ui.tsx | 204 ------------------ web/src/narrations.ts | 68 ++++++ .../[gameId]/[locationSlug]/[drugSlug].tsx | 32 +-- .../pages/[gameId]/[locationSlug]/index.tsx | 34 +-- web/src/pages/[gameId]/end.tsx | 4 +- web/src/pages/[gameId]/event/consequence.tsx | 64 +++--- web/src/pages/[gameId]/event/decision.tsx | 108 +++++----- web/src/pages/[gameId]/index.tsx | 8 +- web/src/pages/[gameId]/travel.tsx | 55 ++--- web/src/pages/[gameId]/turn.tsx | 67 +++--- web/src/pages/_app.tsx | 2 +- web/src/pages/chat.tsx | 7 +- web/src/pages/create.tsx | 8 +- web/src/pages/index.tsx | 21 +- web/src/pages/index.withgames.tsx | 8 +- web/src/pages/leaderboard.tsx | 8 +- web/src/pages/pending/[hash].tsx | 8 +- web/src/utils/market.ts | 2 +- 52 files changed, 886 insertions(+), 780 deletions(-) rename web/src/{hooks => }/dojo/components/useGlobalScores.tsx (100%) rename web/src/{hooks => }/dojo/entities/useGameEntity.tsx (100%) rename web/src/{hooks => }/dojo/entities/useLocationEntity.tsx (100%) rename web/src/{hooks => }/dojo/entities/usePlayerEntity.tsx (97%) rename web/src/{utils/event.ts => dojo/events.ts} (90%) create mode 100644 web/src/dojo/helpers.ts rename web/src/{hooks => }/dojo/index.tsx (98%) rename web/src/{hooks => }/dojo/systems/useSystems.tsx (96%) create mode 100644 web/src/dojo/types.ts delete mode 100644 web/src/hooks/ui.tsx create mode 100644 web/src/narrations.ts diff --git a/scripts/default_auth.sh b/scripts/default_auth.sh index 11faeb8c1..277c7552b 100755 --- a/scripts/default_auth.sh +++ b/scripts/default_auth.sh @@ -29,6 +29,7 @@ SET_NAME_COMPONENTS=("Name") BUY_COMPONENTS=("Drug" "Market" "Name" "Player") SELL_COMPONENTS=("Drug" "Market" "Name" "Player") TRAVEL_COMPONENTS=("Player") +DECIDE_COMPONENTS=("Player" "Drug") for component in ${CREATE_GAME_COMPONENTS[@]}; do sozo auth writer $component create_game --world $WORLD_ADDRESS @@ -58,4 +59,8 @@ for component in ${TRAVEL_COMPONENTS[@]}; do sozo auth writer $component decide --world $WORLD_ADDRESS done +for component in ${DECIDE_COMPONENTS[@]}; do + sozo auth writer $component decide --world $WORLD_ADDRESS +done + echo "Default authorizations have been successfully set." \ No newline at end of file diff --git a/src/components/player.cairo b/src/components/player.cairo index 72fe2c6bb..df06edc4a 100644 --- a/src/components/player.cairo +++ b/src/components/player.cairo @@ -7,11 +7,13 @@ struct Player { game_id: u32, #[key] player_id: ContractAddress, + status: PlayerStatus, location_id: felt252, cash: u128, health: u8, + drug_count: usize, + bag_limit: usize, turns_remaining: usize, - status: PlayerStatus, } #[generate_trait] diff --git a/src/components/risks.cairo b/src/components/risks.cairo index 11586ea62..87a3f8f26 100644 --- a/src/components/risks.cairo +++ b/src/components/risks.cairo @@ -4,13 +4,6 @@ use debug::PrintTrait; use rollyourown::constants::SCALING_FACTOR; -#[derive(Drop, Copy)] -struct TravelResult { - arrested: bool, - mugged: bool, - health_loss: u8, -} - #[derive(Component, Copy, Drop, Serde)] struct Risks { #[key] @@ -28,6 +21,7 @@ impl RisksImpl of RisksTrait { occurs(seed, self.travel) } + #[inline(always)] fn run(ref self: Risks, seed: felt252) -> bool { occurs(seed, self.run) } diff --git a/src/constants.cairo b/src/constants.cairo index 4aaac1ccb..ce0d28225 100644 --- a/src/constants.cairo +++ b/src/constants.cairo @@ -1,17 +1,18 @@ const SCALING_FACTOR: u128 = 10_000; -const TRAVEL_RISK: u8 = 100; // 30% chance of mugged -const RUN_CHANCE: u8 = 30; // 30% chance of successfully getting away +const TRAVEL_RISK: u8 = 30; // 30% chance of mugged +const RUN_CHANCE: u8 = 50; // 50% chance of successfully getting away -const RUN_PENALTY: u8 = 30; // 30% of cash lost -const PAY_PENALTY: u8 = 10; // 10% of cash lost +const BASE_PAYMENT: u128 = 400_0000; // base payment is $400 -// max drug price is $300 -// min drug price is $2 -const MAX_QUANTITY: usize = 50_000; -const MIN_QUANITTY: usize = 20_000; -const MAX_CASH: u128 = 60_000_000_000; // $6Mil -const MIN_CASH: u128 = 1_000_000_000; // $100k +// max drug price is $300 = MAX_CASH / MIN_QUANTITY +// min drug price is $50 = MIN_CASH / MAX_QUANTITY +const MAX_QUANTITY: usize = 500; +const MIN_QUANITTY: usize = 200; +const MAX_CASH: u128 = 100_000_0000; // $100k +const MIN_CASH: u128 = 25_000_0000; // $25k -// cash players start with -const STARTING_CASH: u128 = 20_000_000; // $2000 +// starting stats +const STARTING_CASH: u128 = 2000_0000; // $2000 +const STARTING_BAG_LIMIT: usize = 100; // inventory size +const STARTING_HEALTH: u8 = 100; diff --git a/src/systems/create.cairo b/src/systems/create.cairo index 15da09c3d..8b9721415 100644 --- a/src/systems/create.cairo +++ b/src/systems/create.cairo @@ -19,7 +19,7 @@ mod create_game { use rollyourown::components::location::{Location, LocationTrait}; use rollyourown::constants::{ SCALING_FACTOR, TRAVEL_RISK, RUN_CHANCE, MIN_CASH, MAX_CASH, MIN_QUANITTY, MAX_QUANTITY, - STARTING_CASH + STARTING_CASH, STARTING_HEALTH, STARTING_BAG_LIMIT }; use rollyourown::utils::random; @@ -46,46 +46,37 @@ mod create_game { location_id: felt252, } - fn execute( ctx: Context, start_time: u64, max_players: usize, max_turns: usize ) -> (u32, ContractAddress) { let game_id = ctx.world.uuid(); - - // game entity - set !( - ctx.world, - (Game { - game_id, - start_time, - max_players, - num_players: 1, // caller auto joins - max_turns, - is_finished: false, - creator: ctx.origin, - }) - ); - let seed = starknet::get_tx_info().unbox().transaction_hash; let location_id = LocationTrait::random(seed); - // player entity - set !( - ctx.world, - ( - Player { - game_id, - player_id: ctx.origin, - location_id, - cash: STARTING_CASH, - health: 100, - turns_remaining: max_turns, - status: PlayerStatus::Normal(()), - }, - ) - ); - // TODO: spawn locations with risk profiles balanced - // with market pricing + let player = Player { + game_id, + player_id: ctx.origin, + location_id, + cash: STARTING_CASH, + health: STARTING_HEALTH, + drug_count: 0, + bag_limit: STARTING_BAG_LIMIT, + turns_remaining: max_turns, + status: PlayerStatus::Normal(()), + }; + + let game = Game { + game_id, + start_time, + max_players, + num_players: 1, // caller auto joins + max_turns, + is_finished: false, + creator: ctx.origin, + }; + + set !(ctx.world, (game, player)); + let mut locations = LocationTrait::all(); loop { match locations.pop_front() { diff --git a/src/systems/decide.cairo b/src/systems/decide.cairo index 1608d12ea..93b19a044 100644 --- a/src/systems/decide.cairo +++ b/src/systems/decide.cairo @@ -6,11 +6,13 @@ mod decide { use starknet::ContractAddress; use dojo::world::Context; + use rollyourown::PlayerStatus; - use rollyourown::constants::{RUN_PENALTY, PAY_PENALTY}; + use rollyourown::constants::BASE_PAYMENT; use rollyourown::components::game::{Game, GameTrait}; use rollyourown::components::risks::{Risks, RisksTrait}; use rollyourown::components::player::{Player, PlayerTrait}; + use rollyourown::components::drug::{Drug, DrugTrait}; #[derive(Copy, Drop, Serde, PartialEq)] enum Action { @@ -30,6 +32,8 @@ mod decide { enum Event { Decision: Decision, Consequence: Consequence, + CashLoss: CashLoss, + DrugLoss: DrugLoss, } #[derive(Drop, starknet::Event)] @@ -43,48 +47,105 @@ mod decide { struct Consequence { game_id: u32, player_id: ContractAddress, - outcome: Outcome + outcome: Outcome, } - fn execute(ctx: Context, game_id: u32, action: Action, next_location_id: felt252) { - let game = get !(ctx.world, game_id, Game); - assert(game.tick(), 'game cannot progress'); + #[derive(Drop, starknet::Event)] + struct CashLoss { + game_id: u32, + player_id: ContractAddress, + amount: u128 + } + + #[derive(Drop, starknet::Event)] + struct DrugLoss { + game_id: u32, + player_id: ContractAddress, + drug_id: felt252, + quantity: usize + } + fn execute(ctx: Context, game_id: u32, action: Action, next_location_id: felt252) { let player_id = ctx.origin; let mut player = get !(ctx.world, (game_id, player_id).into(), Player); assert(player.status != PlayerStatus::Normal(()), 'player response not needed'); - let outcome = match action { - Action::Pay => { - emit !(ctx.world, Decision { game_id, player_id, action: Action::Pay }); - - player.cash -= 1; - Outcome::Paid(()) - }, - Action::Run => { - emit !(ctx.world, Decision { game_id, player_id, action: Action::Run }); - - let mut risks = get !(ctx.world, (game_id, player.location_id).into(), Risks); - let seed = starknet::get_tx_info().unbox().transaction_hash; - let got_away = risks.run(seed); - - match got_away { - bool::False => { - player.cash -= 1; - Outcome::Captured(()) - }, - bool::True => { - Outcome::Escaped(()) - } - } - }, + let (outcome, cash_loss) = match action { + Action::Pay => pay(ctx, game_id, player_id, player.cash), + Action::Run => run(ctx, game_id, player_id, player.location_id, player.cash), }; + player.cash -= cash_loss; player.status = PlayerStatus::Normal(()); player.location_id = next_location_id; player.turns_remaining -= 1; - set !(ctx.world, (player)); + set !(ctx.world, (player)); emit !(ctx.world, Consequence { game_id, player_id, outcome }); } + + // Player will hand over either 20% of their cash or $400, which ever is more + fn pay( + ctx: Context, game_id: u32, player_id: ContractAddress, player_cash: u128 + ) -> (Outcome, u128) { + assert(player_cash >= BASE_PAYMENT, 'not enough cash kid'); + let cash_loss = cmp::max(player_cash / 5, BASE_PAYMENT); + + emit !(ctx.world, Decision { game_id, player_id, action: Action::Pay }); + emit !(ctx.world, CashLoss { game_id, player_id, amount: cash_loss }); + (Outcome::Paid(()), cash_loss) + } + + // Player will try to run and can escape. However, if they are captured they lose 50% of everything + fn run( + ctx: Context, + game_id: u32, + player_id: ContractAddress, + location_id: felt252, + player_cash: u128 + ) -> (Outcome, u128) { + let mut risks = get !(ctx.world, (game_id, location_id).into(), Risks); + let seed = starknet::get_tx_info().unbox().transaction_hash; + let got_away = risks.run(seed); + + emit !(ctx.world, Decision { game_id, player_id, action: Action::Run }); + match got_away { + bool::False => { + let cash_loss = player_cash / 2; + halve_drugs(ctx, game_id, player_id); + + emit !(ctx.world, CashLoss { game_id, player_id, amount: cash_loss }); + (Outcome::Captured(()), cash_loss) + }, + bool::True => { + (Outcome::Escaped(()), 0) + } + } + } + + // sorry fren, u jus lost half ur stash, ngmi + fn halve_drugs(ctx: Context, game_id: u32, player_id: ContractAddress) { + let mut drugs = DrugTrait::all(); + loop { + match drugs.pop_front() { + Option::Some(drug_id) => { + let mut drug = get !(ctx.world, (game_id, player_id, *drug_id).into(), Drug); + if (drug.quantity != 0) { + drug.quantity /= 2; + + emit !( + ctx.world, + DrugLoss { + game_id, player_id, drug_id: *drug_id, quantity: drug.quantity + } + ); + set !(ctx.world, (drug)); + } + }, + Option::None(()) => { + break (); + } + }; + }; + } } diff --git a/src/systems/join.cairo b/src/systems/join.cairo index 638182ddc..ebf7bc215 100644 --- a/src/systems/join.cairo +++ b/src/systems/join.cairo @@ -11,7 +11,9 @@ mod join_game { use rollyourown::components::game::Game; use rollyourown::components::player::Player; use rollyourown::components::location::{Location, LocationTrait}; - use rollyourown::constants::{SCALING_FACTOR, STARTING_CASH}; + use rollyourown::constants::{ + SCALING_FACTOR, STARTING_CASH, STARTING_HEALTH, STARTING_BAG_LIMIT + }; #[event] #[derive(Drop, starknet::Event)] @@ -27,48 +29,34 @@ mod join_game { } fn execute(ctx: Context, game_id: u32) -> ContractAddress { + let player_id = ctx.origin; let block_info = starknet::get_block_info().unbox(); - let game = get !(ctx.world, game_id, (Game)); + let mut game = get !(ctx.world, game_id, (Game)); assert(!game.is_finished, 'game is finished'); assert(game.max_players > game.num_players, 'game is full'); assert(game.start_time >= block_info.block_timestamp, 'already started'); + game.num_players += 1; + let seed = starknet::get_tx_info().unbox().transaction_hash; let location_id = LocationTrait::random(seed); - // spawn player into game - set !( - ctx.world, - (Player { - game_id, - player_id: ctx.origin, - location_id, - cash: STARTING_CASH, - health: 100, - turns_remaining: game.max_turns, - status: PlayerStatus::Normal(()), - }) - ); - - // update num players joined - set !( - ctx.world, - (Game { - game_id, - start_time: game.start_time, - max_players: game.max_players, - num_players: game.num_players + 1, - max_turns: game.max_turns, - is_finished: false, - creator: game.creator, - }) - ); - - // emit player joined - emit !( - ctx.world, PlayerJoined { game_id, player_id: ctx.origin, location_id: location_id } - ); - ctx.origin + let player = Player { + game_id, + player_id, + location_id, + cash: STARTING_CASH, + health: STARTING_HEALTH, + drug_count: 0, + bag_limit: STARTING_BAG_LIMIT, + turns_remaining: game.max_turns, + status: PlayerStatus::Normal(()), + }; + + set !(ctx.world, (game, player)); + emit !(ctx.world, PlayerJoined { game_id, player_id, location_id }); + + player_id } } diff --git a/src/systems/trade.cairo b/src/systems/trade.cairo index 622d71144..29d574e0c 100644 --- a/src/systems/trade.cairo +++ b/src/systems/trade.cairo @@ -46,28 +46,30 @@ mod buy { let mut player = get !(ctx.world, (game_id, player_id).into(), Player); assert(player.location_id == location_id, 'player is not at location'); assert(player.can_continue(), 'player cannot trade'); + assert(player.drug_count + quantity <= player.bag_limit, 'no bag space'); let mut market = get !(ctx.world, (game_id, location_id, drug_id).into(), Market); let cost = market.buy(quantity); assert(cost < player.cash, 'not enough cash'); + let mut drug = get !(ctx.world, (game_id, player_id, drug_id).into(), Drug); + // update market market.cash += cost; market.quantity -= quantity; - set !(ctx.world, (market)); // update player player.cash -= cost; - set !(ctx.world, (player)); + player.drug_count += quantity; - let mut player_drug = get !(ctx.world, (game_id, player_id, drug_id).into(), Drug); - player_drug.game_id = game_id; - player_drug.player_id = player_id; - player_drug.drug_id = drug_id; - player_drug.quantity += quantity; - set !(ctx.world, (player_drug)); + // update drug + drug.game_id = game_id; + drug.player_id = player_id; + drug.drug_id = drug_id; + drug.quantity += quantity; + set !(ctx.world, (market, player, drug)); emit !(ctx.world, Bought { game_id, player_id, drug_id, quantity, cost }); } } @@ -122,15 +124,15 @@ mod sell { // update market market.quantity += quantity; market.cash -= payout; - set !(ctx.world, (market)); // update player player.cash += payout; - set !(ctx.world, (player)); + player.drug_count -= quantity; + // update drug drug.quantity -= quantity; - set !(ctx.world, (drug)); + set !(ctx.world, (market, player, drug)); emit !(ctx.world, Sold { game_id, player_id, drug_id, quantity, payout }); } } diff --git a/src/systems/travel.cairo b/src/systems/travel.cairo index 68b59968f..e3587f298 100644 --- a/src/systems/travel.cairo +++ b/src/systems/travel.cairo @@ -10,13 +10,13 @@ mod travel { use rollyourown::PlayerStatus; use rollyourown::components::{game::{Game, GameTrait}, location::Location}; use rollyourown::components::player::{Player, PlayerTrait}; - use rollyourown::components::risks::{Risks, RisksTrait, TravelResult}; + use rollyourown::components::risks::{Risks, RisksTrait}; #[event] #[derive(Drop, starknet::Event)] enum Event { Traveled: Traveled, - RandomEvent: RandomEvent, + AdverseEvent: AdverseEvent, } #[derive(Drop, starknet::Event)] @@ -28,7 +28,7 @@ mod travel { } #[derive(Drop, starknet::Event)] - struct RandomEvent { + struct AdverseEvent { game_id: u32, player_id: ContractAddress, player_status: PlayerStatus, @@ -51,13 +51,14 @@ mod travel { let mut risks = get !(ctx.world, (game_id, next_location_id).into(), Risks); let seed = starknet::get_tx_info().unbox().transaction_hash; + // only mugging for now if risks.travel(seed) { player.status = PlayerStatus::BeingMugged(()); set !(ctx.world, (player)); emit !( ctx.world, - RandomEvent { game_id, player_id, player_status: PlayerStatus::BeingMugged(()) } + AdverseEvent { game_id, player_id, player_status: PlayerStatus::BeingMugged(()) } ); return true; diff --git a/src/tests.cairo b/src/tests.cairo index aa7107fb0..56f7c05d6 100644 --- a/src/tests.cairo +++ b/src/tests.cairo @@ -1,6 +1,5 @@ mod create; mod travel; mod player; -//mod trade; - +mod trade; diff --git a/src/tests/trade.cairo b/src/tests/trade.cairo index 7b5619ccf..97987859c 100644 --- a/src/tests/trade.cairo +++ b/src/tests/trade.cairo @@ -7,119 +7,48 @@ use debug::PrintTrait; use starknet::{ContractAddress, syscalls::deploy_syscall}; use starknet::class_hash::{ClassHash, Felt252TryIntoClassHash}; -use starknet::contract_address_const; -use dojo::database::query::{IntoPartitioned, IntoPartitionedQuery}; use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; -use dojo::world::Context; -use dojo::string::ShortStringTrait; -use dojo::integer::u250Trait; -use rollyourown::components::{ - player::Cash, drug::Drug, location::Location, market::{Market, MarketTrait} -}; -use rollyourown::tests::spawn::{spawn_game, spawn_location, spawn_player}; +use dojo::test_utils::spawn_test_world; + +use rollyourown::PlayerStatus; +use rollyourown::components::drug::Drug; +use rollyourown::components::player::Player; +use rollyourown::tests::create::{spawn_game, spawn_player}; use rollyourown::constants::SCALING_FACTOR; -const DRUG_ID: felt252 = 0; -const QUANTITY: usize = 50; +const DRUG_ID: felt252 = 0x57656564; // weed +const QUANTITY: usize = 3; #[test] #[available_gas(100000000)] -fn test_player_buy() { - let (world_address, game_id, player_id) = spawn_game(); - let location_id = spawn_location(world_address, game_id); - +fn test_trade() { + let (world_address, game_id, player_id) = spawn_game(); // creator auto joins let world = IWorldDispatcher { contract_address: world_address }; - // travel to location - let mut player_travel_calldata = array::ArrayTrait::::new(); - player_travel_calldata.append(game_id); - player_travel_calldata.append(location_id); - world.execute('Travel'.into(), player_travel_calldata.span()); + let player = get !(world, (game_id, player_id).into(), (Player)); - // buy from market + // market buy 3 weed let mut buy_calldata = array::ArrayTrait::::new(); - buy_calldata.append(game_id); - buy_calldata.append(location_id); + buy_calldata.append(game_id.into()); + buy_calldata.append(player.location_id); buy_calldata.append(DRUG_ID); buy_calldata.append(QUANTITY.into()); - world.execute('Buy'.into(), buy_calldata.span()); - - // verify player has drug in inventory - let mut res = world - .entity('Drug'.into(), (game_id, (player_id, DRUG_ID)).into_partitioned(), 0, 0); - assert(res.len() > 0, 'no drug'); - let drug = serde::Serde::::deserialize(ref res).expect('deserialization failed'); - assert(drug.quantity == QUANTITY, 'incorrect quantity'); - - // calc market cost - let market = Market { cash: 100 * SCALING_FACTOR, quantity: 1000 }; - let cost = market.buy(QUANTITY); - - // verify player has cash - cost - let mut res = world.entity('Cash'.into(), (game_id, (player_id)).into_partitioned(), 0, 0); - assert(res.len() > 0, 'no cash'); - let cash = serde::Serde::::deserialize(ref res).expect('deserialization failed'); - assert(cash.amount == (100 * SCALING_FACTOR - cost), 'incorrect cash'); + world.execute('buy'.into(), buy_calldata); + + let player = get !(world, (game_id, player_id).into(), (Player)); + let player_drug = get !(world, (game_id, player_id, DRUG_ID).into(), (Drug)); + assert(player.drug_count == QUANTITY, 'wrong drug count'); + assert(player_drug.quantity == QUANTITY, 'wrong purchase amount'); + + // market sell 1 weed + let mut sell_calldata = array::ArrayTrait::::new(); + sell_calldata.append(game_id.into()); + sell_calldata.append(player.location_id); + sell_calldata.append(DRUG_ID); + sell_calldata.append(1); + world.execute('sell'.into(), sell_calldata); + + let player = get !(world, (game_id, player_id).into(), (Player)); + assert(player.drug_count == QUANTITY - 1, 'wrong sell amount'); } -// FIXME -// #[test] -// #[available_gas(100000000)] -// fn test_player_sell() { -// let (world_address, game_id, player_id) = spawn_game(); -// let location_id = spawn_location(world_address, game_id); - -// let world = IWorldDispatcher { contract_address: world_address }; - -// let ctx = Context { -// world, -// caller_account: world.contract_address, -// caller_system: 'Sell'.into(), -// execution_role: AuthRole { -// id: 'DrugWriter'.into() -// }, -// }; -// // give player drug -// let mut calldata = array::ArrayTrait::new(); -// serde::Serde::serialize(@Drug { id: 0.into(), quantity: QUANTITY }, ref calldata); -// World::set_entity( -// ctx, -// 'Drug'.into(), -// (game_id, (player_id, DRUG_ID)).into_partitioned(), -// 0, -// ArrayTrait::span(@calldata) -// ); - -// // travel to location -// let mut player_travel_calldata = array::ArrayTrait::::new(); -// player_travel_calldata.append(game_id); -// player_travel_calldata.append(location_id); -// world.execute('Travel'.into(), player_travel_calldata.span()); - -// // sell to market -// let mut sell_calldata = array::ArrayTrait::::new(); -// sell_calldata.append(game_id); -// sell_calldata.append(location_id); -// sell_calldata.append(DRUG_ID); -// sell_calldata.append(QUANTITY.into()); -// world.execute('Sell'.into(), sell_calldata.span()); - -// // verify player has no drug -// let mut res = world -// .entity('Drug'.into(), (game_id, (player_id, DRUG_ID)).into_partitioned(), 0, 0); -// assert(res.len() > 0, 'no drug'); -// let drug = serde::Serde::::deserialize(ref res).expect('deserialization failed'); -// assert(drug.quantity == 0, 'incorrect quantity'); -// // FIXME: keep getting gas withdraw errors -// // // calc market cost -// // let market = Market { cash: 100 * SCALING_FACTOR, quantity: 1000}; -// // let payout = market.sell(QUANTITY); - -// // // verify player has cash + payout -// // let mut res = world.entity('Cash'.into(), (game_id, (player_id)).into_partitioned(), 0, 0); -// // assert(res.len() > 0, 'no cash'); -// // let cash = serde::Serde::::deserialize(ref res).expect('deserialization failed'); -// // assert(cash.amount == (100 * SCALING_FACTOR + payout), 'incorrect cash'); -// } - - diff --git a/web/src/components/Header.tsx b/web/src/components/Header.tsx index 33962c228..469419bda 100644 --- a/web/src/components/Header.tsx +++ b/web/src/components/Header.tsx @@ -1,26 +1,16 @@ -import { Clock, Gem, Bag, Chat, Home, Link, Sound, Arrow } from "./icons"; -import { Box, Button, Divider, Flex, HStack, Text } from "@chakra-ui/react"; +import { Clock, Gem, Bag, Arrow } from "./icons"; +import { Divider, HStack, Text } from "@chakra-ui/react"; import { useEffect, useState } from "react"; import { IsMobile, generatePixelBorderPath } from "@/utils/ui"; import { useRouter } from "next/router"; -import { - useSoundStore, - Sounds, - toggleIsMuted, - playSound, - stopSound, - initSoundStore, -} from "@/hooks/sound"; -import { useUiStore, setIsConnected } from "@/hooks/ui"; +import { initSoundStore } from "@/hooks/sound"; import HeaderButton from "@/components/HeaderButton"; import MediaPlayer from "@/components/MediaPlayer"; import MobileMenu from "@/components/MobileMenu"; -import { play } from "@/hooks/media"; -import { usePlayerEntityQuery, Entity } from "@/generated/graphql"; -import { usePlayerEntity } from "@/hooks/dojo/entities/usePlayerEntity"; -import { useGameEntity } from "@/hooks/dojo/entities/useGameEntity"; +import { usePlayerEntity } from "@/dojo/entities/usePlayerEntity"; +import { useGameEntity } from "@/dojo/entities/useGameEntity"; import { formatCash } from "@/utils/ui"; -import { useDojo } from "@/hooks/dojo"; +import { useDojo } from "@/dojo"; // TODO: constrain this on contract side const MAX_INVENTORY = 100; @@ -35,18 +25,15 @@ const Header = ({ back }: HeaderProps) => { const [inventory, setInventory] = useState(0); const { account } = useDojo(); - const { player: playerEntity, isFetched: isFetchedPlayer } = usePlayerEntity({ + const { player: playerEntity } = usePlayerEntity({ gameId, address: account?.address, }); - const { game: gameEntity, isFetched: isFetchedGame } = useGameEntity({ + const { game: gameEntity } = useGameEntity({ gameId, }); const isMobile = IsMobile(); - const isMuted = useSoundStore((state) => state.isMuted); - const isConnected = useUiStore((state) => state.isConnected); - const hasNewMessages = true; useEffect(() => { const init = async () => { @@ -89,7 +76,7 @@ const Header = ({ back }: HeaderProps) => { - {inventory} + {inventory === 100 ? "Full" : `${inventory}/100`} diff --git a/web/src/components/Inventory.tsx b/web/src/components/Inventory.tsx index 89be57fd5..6f2c8220d 100644 --- a/web/src/components/Inventory.tsx +++ b/web/src/components/Inventory.tsx @@ -8,10 +8,10 @@ import { } from "@chakra-ui/react"; import React from "react"; -import { usePlayerEntity } from "@/hooks/dojo/entities/usePlayerEntity"; +import { usePlayerEntity } from "@/dojo/entities/usePlayerEntity"; import { useRouter } from "next/router"; -import { getDrugById } from "@/hooks/ui"; -import { useDojo } from "@/hooks/dojo"; +import { useDojo } from "@/dojo"; +import { getDrugById } from "@/dojo/helpers"; export const Inventory = ({ ...props }: StyleProps) => { const router = useRouter(); @@ -41,7 +41,7 @@ export const Inventory = ({ ...props }: StyleProps) => { }} > - {playerEntity?.drugs.length === 0 ? ( + {playerEntity?.drugCount === 0 ? ( Your bag is empty ) : ( playerEntity?.drugs.map((drug, index) => { diff --git a/web/src/components/Layout.tsx b/web/src/components/Layout.tsx index cf9b39ca6..58ece2c5c 100644 --- a/web/src/components/Layout.tsx +++ b/web/src/components/Layout.tsx @@ -12,26 +12,31 @@ import { ReactNode } from "react"; import Header from "./Header"; import { motion } from "framer-motion"; +import CrtEffect from "./CrtEffect"; + export interface LayoutProps { - title: string; - map: ReactNode; - imageSrc: string; - prefixTitle: string; + leftPanelProps?: LeftPanelProps; showBack?: boolean; actions?: ReactNode; + showMap?: boolean; children: ReactNode; + isSinglePanel?: boolean; } -import CrtEffect from "./CrtEffect"; +export interface LeftPanelProps { + title: string; + prefixTitle?: string; + imageSrc?: string; + map?: ReactNode; +} const Layout = ({ - title, - prefixTitle, - map, - imageSrc, + leftPanelProps, showBack, + showMap, children, -}: Partial) => { + isSinglePanel = false, +}: LayoutProps) => { return ( <>
- - {children} + {!isSinglePanel && } + {children} @@ -66,12 +65,7 @@ const LeftPanel = ({ map, imageSrc, ...props -}: { - title?: string; - prefixTitle?: string; - map?: ReactNode; - imageSrc?: string; -} & StyleProps) => { +}: Partial & StyleProps) => { return ( { +export const Callout = ({ location }: { location: Location }) => { return ( { viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg" > - + { fill="#11ED83" /> - + { fill="#11ED83" /> - + { fill="#11ED83" /> - + { /> - + { fill="#11ED83" /> - + void; + onSelect: (target: Location) => void; }) => ( onSelect(Locations.Brooklyn)} + onClick={() => onSelect(Location.Brooklyn)} cursor="pointer" d="M74.5421 213.359H74.3667L74.2297 213.469L72.4986 214.853H65.5236L61.9527 206.998L60.3885 193.839L65.6573 179.881L74.8394 162.986H77.0794L79.286 163.692L85.6821 167.493L85.7308 167.522L85.7848 167.539L90.7352 169.127L90.8923 169.178L91.0485 169.125L112.532 161.839L112.551 161.832L112.571 161.824L155.569 143.105L164.752 153.962L171.846 165.577L171.861 165.601L171.878 165.623L176.619 171.727L172.86 172.745L172.767 172.77L172.69 172.828L160.663 181.872L143.873 191.853L143.629 191.998V192.282V193.31V193.741L144.055 193.804L150.541 194.769V197.104L149.575 198.86L149.439 199.107L149.58 199.352L150.541 201.01V202.596L149.65 203.973L147.34 205.513H144.129H143.88L143.73 205.711L141.395 208.793L141.294 208.927V209.095V215.755L139.178 218.753L135.628 221.859H132.766V219.09V218.766L132.47 218.633L125.372 215.457L125.274 215.414H125.168H120.684H120.184V215.914V217.489L115.345 222.597L113.665 223.353H107.589L102.836 221.891L84.3716 213.405L84.2722 213.359H84.1628H80.9966L79.8328 212.278L79.4797 211.95L79.139 212.291L78.0712 213.359H74.5421Z" /> onSelect(Locations.Queens)} + onClick={() => onSelect(Location.Queens)} cursor="pointer" d="M188.692 98.475L194.358 100.675L194.867 103.537L194.912 103.787L195.14 103.899L197.719 105.157V109.587V109.983L198.104 110.074L200.37 110.61V112.249L199.546 112.798L199.233 113.007L199.347 113.366L199.835 114.9L199.927 115.189L200.226 115.241L203.338 115.782L208.486 119.008L208.568 119.06L208.663 119.077L212.506 119.77V121.356L210.435 124.295L210.214 124.609L210.46 124.904L212.623 127.485L212.927 127.848L213.306 127.564L214.568 126.618H218.621L220.758 128.315L222.271 131.801V134.454L220.401 136.829L220.227 137.05L220.326 137.314L221.23 139.71L219.021 142.7L218.923 142.832V142.997V144.88V145.38H219.423H222.458L223.387 147.297V153.378H218.795H218.603L218.461 153.506L217.208 154.633H214.645L212.284 151.616L211.981 151.229L211.589 151.526L208.45 153.897L208.251 154.047V154.296V157.086V157.346L208.465 157.496L210.344 158.811V160.5L209.172 162.375H205.613H205.327L205.182 162.622L203.932 164.747H202.265H202.05L201.903 164.902L200.655 166.211H197.464V164.764L198 163.022L198.065 162.81L197.952 162.62L196.626 160.388L196.332 159.892L195.883 160.254L187.304 167.159L186.831 167.539L187.295 167.93L189.349 169.664V172.631L188.677 173.256H186.887L186.34 172.544L186.205 172.37L185.986 172.351L179.714 171.817L178.024 168.308L178.009 168.277L177.991 168.249L169.138 154.866L169.133 154.857L159.636 139.826L168.265 129.294L168.346 129.196L168.37 129.072L168.718 127.258L168.757 127.057L168.643 126.886L167.751 125.548V121.514V121.402L167.703 121.3L163.485 112.382L162.817 105.496L167.631 99.8683L167.751 99.728V99.5433V89.1508V88.9906L167.658 88.8602L166.705 87.5259V83.0826V82.5826H166.205H164.17L158.893 74.7004V71.5172L161.807 67.1798L161.892 67.0533V66.901V63.5133L167.369 60.7748H171.52L179.654 65.1086V67.6608L178.696 68.5589L178.538 68.7071V68.9237V72.4111V72.6127L178.678 72.7579L180.352 74.4959V77.0145V77.3378L180.647 77.4705L183.36 78.6916L185.443 80.6485V83.0826V83.3126L185.618 83.4623L188.059 85.5547L188.23 85.7009L188.452 85.6705L191.521 85.252L191.667 85.2321L191.779 85.1362L193.592 83.5826H196.735L198.277 85.3616V87.3375L194.629 88.6816L194.617 88.6861L194.605 88.6912L192.831 89.4514L190.644 88.6793L190.207 88.5253L190.02 88.9485C189.751 89.5553 189.349 90.4834 189.012 91.2963C188.845 91.7021 188.692 92.0834 188.58 92.3829C188.524 92.5321 188.477 92.6666 188.443 92.7762C188.426 92.8308 188.41 92.8848 188.398 92.9346C188.389 92.9745 188.373 93.0479 188.373 93.1265C188.373 93.3489 188.485 93.5491 188.554 93.6604C188.639 93.7972 188.748 93.9382 188.857 94.0679C189.016 94.257 189.196 94.445 189.349 94.5954V96.2645L188.446 97.7489L188.135 98.2589L188.692 98.475Z" /> onSelect(Locations.Central)} + onClick={() => onSelect(Location.Central)} cursor="pointer" d="M103.019 155.7L102.948 155.711L102.883 155.74L96.675 158.562H89.1351L78.0715 154.356L79.0064 150.532L83.2865 141.211L83.2907 141.201L83.2946 141.192L88.3529 128.88L94.5456 116.209L105.139 99.2217L105.161 99.1857L105.177 99.1464L112.249 81.8506L112.276 81.7822L112.283 81.7086L113.248 71.5359L116.541 74.1234L116.859 74.3727L117.169 74.1153L123.719 68.688H129.307L135.362 70.2019L135.422 70.2169H135.484H141.408H141.574L141.706 70.1182L144.918 67.7324H147.792L150.997 73.9591L151.009 73.9824L151.023 74.0043L160.049 87.7804L160.585 90.3724L159.467 98.1082L157.356 101.363L153.756 101.903L153.331 101.967V102.397V104.786V104.988L153.471 105.134L156.102 107.855V113.386V113.452L156.119 113.516L158.873 123.772V127.056L156.915 128.248L156.885 128.267L156.857 128.289L144.518 138.445L136.882 140.519L136.828 140.534L136.778 140.56C134.707 141.66 131.585 143.328 128.952 144.76C127.636 145.476 126.44 146.134 125.558 146.632C125.117 146.881 124.751 147.093 124.487 147.252C124.356 147.332 124.244 147.401 124.16 147.458C124.118 147.486 124.078 147.514 124.043 147.541L124.041 147.542C124.028 147.553 124 147.574 123.969 147.604C123.965 147.606 123.96 147.609 123.955 147.611C123.907 147.636 123.83 147.671 123.723 147.715C123.511 147.803 123.209 147.917 122.834 148.051C122.085 148.319 121.067 148.66 119.942 149.026C117.693 149.759 115.03 150.59 113.264 151.131L113.177 151.157L113.105 151.212L108.24 154.955L103.019 155.7Z" /> onSelect(Locations.Bronx)} + onClick={() => onSelect(Location.Bronx)} cursor="pointer" d="M142.29 24.7098V16.6864L142.794 15.463H146.035L146.833 15.9951V22.7031V23.0533L147.162 23.173L149.082 23.8714L149.948 25.516L150.033 25.6778L150.203 25.7465L153.738 27.1798L154.426 27.4587V26.7165V18.2831L155.323 17.0875H159.733V28.7232V28.9399L159.891 29.0881L165.911 34.726L165.982 34.7924L166.073 34.8275L171.2 36.8062V40.2855V40.3967L171.247 40.4975L173.302 44.8879V48.7678H171.7H171.304L171.213 49.1533L170.482 52.2602L167.101 55.5523H163.099H162.929L162.795 55.656L161.594 56.5797L152.316 61.0294L149.3 61.8777L149.231 61.897L149.171 61.9345L140.692 67.2102H135.744L130.078 65.6047L130.011 65.5857H129.941H123.252H123.105L122.982 65.6655L116.021 70.1533L111.808 66.8002V60.5435V60.4007L111.733 60.2795L109.61 56.8651V40.8588V40.6424L109.452 40.4943L104.928 36.2468V34.361V34.1398L104.764 33.991L102.826 32.2287V30.8555L104.764 29.0931L104.871 28.9961L104.91 28.857L105.763 25.7831H110.639H110.988L111.108 25.456L111.749 23.7165L113.459 21.5786H115.131L117.664 23.8405L117.885 24.038L118.164 23.9387L122.464 22.4098L122.797 22.2915V21.9387V20.432H125.852L126.436 20.8859L126.874 22.8139L126.931 23.0634L127.166 23.1634L130.319 24.5012L130.413 24.5409H130.515H132.426H132.926V24.0409V17.5881L133.799 12.7875H135.295L136.14 15.8286L136.708 19.9995L136.72 20.0851L136.76 20.1617L138.193 22.9328L138.255 23.0526L138.369 23.1249L141.522 25.1316L142.29 25.6207V24.7098Z" /> onSelect(Locations.Jersey)} + onClick={() => onSelect(Location.Jersey)} cursor="pointer" d="M56.1862 32.287L57.2874 30.1847H59.69L60.8412 30.8855L62.3429 29.3338L66.2971 28.6331L67.2982 26.2305H69.6007L70.5517 28.6331L73.5549 30.5351L78.8106 31.2359L79.4112 33.8387L81.3633 30.1847H83.7158L85.6679 31.6363H88.4209L89.1717 28.0825H91.6744L93.2261 29.3338V32.287L94.8779 33.0378L95.4284 35.8408H96.9801L98.3316 35.2401H101.785L102.937 39.4447L103.187 48.3542L103.988 49.3553V53.6099L103.187 57.8144L101.335 59.316L100.934 66.5738L101.785 67.3246V73.5814L100.934 74.182L101.785 75.4334V79.1874L102.636 80.4888V83.492L101.335 84.3429L100.934 88.6976L97.6809 94.8543L94.5275 95.4048L93.5264 97.6573L82.2142 114.175L74.2056 129.291L59.69 154.468L57.2374 160.024L47.3267 169.885L46.626 175.741H44.2734L43.2724 173.639L41.5705 174.94L38.3671 175.291L38.0668 178.494L36.6152 179.796H33.2616L32.711 173.639L30.2083 172.688V168.383L31.81 167.683V165.48L30.9591 164.579V161.626L31.81 160.525L30.2083 160.024L29.5076 158.523L26.6545 157.271L24.5022 158.873H22.3498L19.2465 155.77V153.417L20.2476 151.415L21.6491 150.514L20.2476 149.763L19.697 146.51L17.2444 145.859L17.5447 141.655L18.5458 139.302L19.2465 135.548L21.2487 133.246H23.7013L25.7035 135.148L26.9048 136.95L28.056 135.548L29.5076 134.897L30.2083 131.243L32.2605 130.843L33.2616 129.291H38.7174L41.5705 131.243L42.4715 130.242H45.3246L46.2756 131.243L54.2842 130.843L55.8859 129.291L55.4855 127.89L51.331 129.792H48.1276L48.9284 122.684L48.1276 115.276L49.2788 112.223L51.9317 111.622L50.9806 110.621L51.331 108.419L55.4855 107.868L55.8859 106.667L51.9317 101.361L51.6313 98.2079L50.6803 99.0087L46.2756 98.6083V95.4048L49.5791 94.4538L44.0232 93.7531V90.4996L45.3246 89.048L50.6803 88.6976L51.331 85.2439L56.4866 84.3429L54.9349 82.2907L47.2266 81.6901V79.1874L49.5791 78.5868L50.2799 76.1842L53.2331 75.133H59.1394L62.9435 73.9318L66.8978 71.9296L67.6986 65.4727L65.5463 61.4683L63.5942 62.9199H58.2384V60.3171L59.69 59.6664L60.8412 57.3639L59.69 55.5119L57.2874 54.7611V52.1083L59.69 51.7078L60.1405 50.4065L61.0915 49.3553L63.5942 48.6045L64.1948 44.1497L66.5974 43.7493L68.4995 42.548L68.95 39.4447L70.1513 38.3935H74.9064L75.457 40.3957H76.7083V35.2401H70.5517L69.6007 35.8408H58.2384L56.1862 34.6895V32.287Z" /> onSelect(Locations.Coney)} + onClick={() => onSelect(Location.Coney)} cursor="pointer" d="M201.505 223.521V227.182H199.568V224.99V224.521L199.1 224.491L194.794 224.215V219.91V219.41H194.294H190.071H189.762L189.624 219.687L189.212 220.512H187.793L187.238 218.948L187.187 218.805L187.066 218.714L182.721 215.471L182.588 215.371H182.422H180.831H180.57L180.421 215.584L179.285 217.207H177.853L177.562 214.497L178.331 213.873L178.732 213.546L178.392 213.156L176.74 211.259L176.545 211.035L176.255 211.099L174.656 211.455H168.929L168.076 209.321L167.854 208.766L167.347 209.083L166.549 209.581L164.579 209.076L163.767 207.603V203.264L164.696 199.487L165.608 198.624H172.537L173.429 199.569L173.865 200.03L174.211 199.498L175.129 198.091L175.205 197.975L175.21 197.837L175.632 186.63L176.591 185.527H180.221L182.397 188.275L182.547 188.465H182.789H184.38H184.694L184.83 188.183L185.611 186.568H190.071H190.571V186.068V179.591H197.364L197.956 193.555L197.974 193.968L198.383 194.029L202.354 194.613L207.801 201.196L208.1 201.558L208.48 201.282L209.695 200.398H214.121L214.791 204.808L214.829 205.059L215.054 205.177L216.378 205.865L216.817 207.84L216.887 208.158L217.207 208.222L219.268 208.634L221.272 209.795V211.587V211.846L221.484 211.996L222.313 212.581V215.305L221.409 216.262L221.256 216.424L221.274 216.646L221.58 220.44L221.6 220.687L221.809 220.821L223.108 221.653V223.308L221.877 224.49H218.284H218.086L217.942 224.625L217.146 225.371L215.383 225.05L211.413 224.195L211.413 224.195L211.399 224.192L205.283 223.053L203.362 221.482L202.924 221.123L202.622 221.602L201.582 223.255L201.505 223.377V223.521Z" /> diff --git a/web/src/components/map/Map.tsx b/web/src/components/map/Map.tsx index 15d2a7434..047c3f33d 100644 --- a/web/src/components/map/Map.tsx +++ b/web/src/components/map/Map.tsx @@ -1,30 +1,30 @@ import { Flex, Image, useBreakpointValue } from "@chakra-ui/react"; import { motion, useAnimate } from "framer-motion"; import { useEffect } from "react"; -import { Locations } from "@/hooks/state"; import { HitBox } from "./HitBox"; import { Outline } from "./Outline"; import { Markers } from "./Markers"; +import { Location } from "@/dojo/types"; type CoordinateType = { - [key in Locations]: { x: number; y: number }; + [key in Location]: { x: number; y: number }; }; const coordinate: CoordinateType = { - [Locations.Queens]: { x: -150, y: 150 }, - [Locations.Jersey]: { x: 150, y: 150 }, - [Locations.Bronx]: { x: 0, y: 300 }, - [Locations.Central]: { x: 0, y: 150 }, - [Locations.Coney]: { x: -150, y: -0 }, - [Locations.Brooklyn]: { x: 0, y: -0 }, + [Location.Queens]: { x: -150, y: 150 }, + [Location.Jersey]: { x: 150, y: 150 }, + [Location.Bronx]: { x: 0, y: 300 }, + [Location.Central]: { x: 0, y: 150 }, + [Location.Coney]: { x: -150, y: -0 }, + [Location.Brooklyn]: { x: 0, y: -0 }, }; export const Map = ({ highlight, onSelect, }: { - highlight?: Locations; - onSelect: (selected: Locations) => void; + highlight?: Location; + onSelect: (selected: Location) => void; }) => { const [scope, animate] = useAnimate(); const isMobile = useBreakpointValue([true, false]); diff --git a/web/src/components/map/Markers.tsx b/web/src/components/map/Markers.tsx index 3b2d934ee..e6370ac1a 100644 --- a/web/src/components/map/Markers.tsx +++ b/web/src/components/map/Markers.tsx @@ -1,7 +1,7 @@ +import { Location } from "@/dojo/types"; import { Icon } from "@chakra-ui/react"; -import { Locations } from "@/hooks/state"; -export const Markers = ({ location }: { location?: Locations }) => { +export const Markers = ({ location }: { location?: Location }) => { return ( { viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg" > - + { fill="#0CA85D" /> - + { fill="#0CA85D" /> - + { fill="#0CA85D" /> - + { /> - + { fill="#0CA85D" /> - + { +export const Outline = ({ location }: { location?: Location }) => { if (!location) { return <>; } @@ -41,7 +41,7 @@ export const Outline = ({ location }: { location?: Locations }) => { ); }; -const SvgHighlight = ({ location }: { location: Locations }) => { +const SvgHighlight = ({ location }: { location: Location }) => { return ( { xmlns="http://www.w3.org/2000/svg" > - + { /> - + { - + { /> - + getNarration(Outcome.Paid, isInitial), + }, + { + name: "Captured", + type: Outcome.Captured, + imageSrc: "/images/events/smoking_gun.gif", + getNarration: (isInitial: boolean) => + getNarration(Outcome.Captured, isInitial), + }, + { + name: "Escaped", + type: Outcome.Escaped, + imageSrc: "/images/sunset.png", + getNarration: (isInitial: boolean) => + getNarration(Outcome.Escaped, isInitial), + }, +]; + +function findBy(array: T[], key: keyof T, value: any): T | undefined { + return array.find((item) => item[key] === value); +} + +export function getLocationByType(type: Location): LocationInfo { + return findBy(locations, "type", type) || locations[0]; +} + +export function getLocationById(id: string): LocationInfo { + return findBy(locations, "id", id) || locations[0]; +} + +export function getLocationBySlug(slug: string): LocationInfo { + return findBy(locations, "slug", slug) || locations[0]; +} + +export function getDrugById(id: string): DrugInfo { + return findBy(drugs, "id", id) || drugs[0]; +} + +export function getDrugBySlug(slug: string): DrugInfo { + return findBy(drugs, "slug", slug) || drugs[0]; +} + +export function getDrugByType(type: Drug): DrugInfo { + return findBy(drugs, "type", type) || drugs[0]; +} + +export function getOutcomeByType(type: Outcome): OutcomeInfo { + return findBy(outcomes, "type", type) || outcomes[0]; +} diff --git a/web/src/hooks/dojo/index.tsx b/web/src/dojo/index.tsx similarity index 98% rename from web/src/hooks/dojo/index.tsx rename to web/src/dojo/index.tsx index 5eb409fbe..617a159c7 100644 --- a/web/src/hooks/dojo/index.tsx +++ b/web/src/dojo/index.tsx @@ -13,7 +13,7 @@ import { shortString, TransactionStatus, } from "starknet"; -import { useBurner } from "../burner"; +import { useBurner } from "../hooks/burner"; export const SCALING_FACTOR = 10000; export const REFETCH_INTERVAL = 1000; // really need graphql subscriptions... diff --git a/web/src/hooks/dojo/systems/useSystems.tsx b/web/src/dojo/systems/useSystems.tsx similarity index 96% rename from web/src/hooks/dojo/systems/useSystems.tsx rename to web/src/dojo/systems/useSystems.tsx index b08097624..a2a251b35 100644 --- a/web/src/hooks/dojo/systems/useSystems.tsx +++ b/web/src/dojo/systems/useSystems.tsx @@ -1,7 +1,7 @@ -import { Action } from "@/hooks/state"; -import { BaseEventData, parseEvent, RyoEvents } from "@/utils/event"; import { useCallback } from "react"; import { useDojo } from ".."; +import { BaseEventData, parseEvent, RyoEvents } from "../events"; +import { Action } from "../types"; export interface SystemsInterface { create: ( @@ -83,7 +83,7 @@ export const useSystems = (): SystemsInterface => { let result = { hash: receipt.transaction_hash } as SystemExecuteResult; try { - result.event = parseEvent(receipt, RyoEvents.RandomEvent); + result.event = parseEvent(receipt, RyoEvents.AdverseEvent); } catch (err) { // no random event occured } diff --git a/web/src/dojo/types.ts b/web/src/dojo/types.ts new file mode 100644 index 000000000..f3433c70a --- /dev/null +++ b/web/src/dojo/types.ts @@ -0,0 +1,51 @@ +export enum Location { + Queens, + Bronx, + Brooklyn, + Coney, + Jersey, + Central, +} + +export enum Drug { + Acid, + Weed, + Ludes, + Speed, + Heroin, + Cocaine, +} + +export enum Action { + Pay, + Run, +} + +export enum Outcome { + Paid, + Escaped, + Captured, +} + +export interface LocationInfo { + type: Location; + name: string; + slug: string; + id: string; + icon: React.FC; +} + +export interface DrugInfo { + type: Drug; + name: string; + slug: string; + id: string; + icon: React.FC; +} + +export interface OutcomeInfo { + type: Outcome; + name: string; + imageSrc: string; + getNarration: (isInitial: boolean) => string; +} diff --git a/web/src/generated/graphql.ts b/web/src/generated/graphql.ts index 1d3eb3a91..461d7c3e2 100644 --- a/web/src/generated/graphql.ts +++ b/web/src/generated/graphql.ts @@ -375,7 +375,9 @@ export type NameWhereInput = { export type Player = { __typename?: "Player"; + bag_limit?: Maybe; cash?: Maybe; + drug_count?: Maybe; entity?: Maybe; game_id?: Maybe; health?: Maybe; @@ -403,7 +405,9 @@ export type PlayerOrder = { }; export enum PlayerOrderOrderField { + BagLimit = "BAG_LIMIT", Cash = "CASH", + DrugCount = "DRUG_COUNT", GameId = "GAME_ID", Health = "HEALTH", LocationId = "LOCATION_ID", @@ -413,12 +417,24 @@ export enum PlayerOrderOrderField { } export type PlayerWhereInput = { + bag_limit?: InputMaybe; + bag_limitGT?: InputMaybe; + bag_limitGTE?: InputMaybe; + bag_limitLT?: InputMaybe; + bag_limitLTE?: InputMaybe; + bag_limitNEQ?: InputMaybe; cash?: InputMaybe; cashGT?: InputMaybe; cashGTE?: InputMaybe; cashLT?: InputMaybe; cashLTE?: InputMaybe; cashNEQ?: InputMaybe; + drug_count?: InputMaybe; + drug_countGT?: InputMaybe; + drug_countGTE?: InputMaybe; + drug_countLT?: InputMaybe; + drug_countLTE?: InputMaybe; + drug_countNEQ?: InputMaybe; game_id?: InputMaybe; game_idGT?: InputMaybe; game_idGTE?: InputMaybe; @@ -778,6 +794,7 @@ export type PlayerEntityQuery = { cash?: any | null; health?: any | null; turns_remaining?: any | null; + drug_count?: any | null; location_id?: any | null; status?: any | null; } @@ -1015,6 +1032,7 @@ export const PlayerEntityDocument = ` cash health turns_remaining + drug_count location_id status } diff --git a/web/src/graphql/entities.graphql b/web/src/graphql/entities.graphql index 94ffcce89..3fd1d82b1 100644 --- a/web/src/graphql/entities.graphql +++ b/web/src/graphql/entities.graphql @@ -26,6 +26,7 @@ query PlayerEntity($gameId: String!, $playerId: String!) { cash health turns_remaining + drug_count location_id status } diff --git a/web/src/hooks/burner.tsx b/web/src/hooks/burner.tsx index 1b6663522..8984150dc 100644 --- a/web/src/hooks/burner.tsx +++ b/web/src/hooks/burner.tsx @@ -45,7 +45,9 @@ export const useBurner = () => { admin.getTransactionReceipt(storage[firstAddr].deployTx).catch(() => { setAccount(undefined); Storage.remove("burners"); - throw new Error("burners not deployed, chain may have restarted"); + //throw new Error("burners not deployed, chain may have restarted"); + console.log("burners not deployed, resetting local storage"); + return; }); // set active account diff --git a/web/src/hooks/state.tsx b/web/src/hooks/state.tsx index d307060ba..b5f9e6c12 100644 --- a/web/src/hooks/state.tsx +++ b/web/src/hooks/state.tsx @@ -1,25 +1,8 @@ import { create } from "zustand"; +import { Drug, Outcome } from "../dojo/types"; -export enum Locations { - Queens = "Queens", - Bronx = "The Bronx", - Brooklyn = "Brooklyn", - Coney = "Coney Island", - Jersey = "Jersey City", - Central = "Central Park", -} - -export enum Drugs { - Acid = "Acid", - Weed = "Weed", - Ludes = "Ludes", - Speed = "Speed", - Heroin = "Heroin", - Cocaine = "Cocaine", -} - -export type DrugsType = { - [key in Drugs]: { +export type DrugType = { + [key in Drug]: { quantity: number; }; }; @@ -34,31 +17,26 @@ export type TradeType = { direction: TradeDirection; }; -export enum Action { - Pay, - Run, -} - -export enum Outcome { - Paid, - Escaped, - Captured, -} - export interface PlayerStore { outcomes: Outcome[]; - trades: Map; + history: Outcome[]; + trades: Map; addOutcome: (outcome: Outcome) => void; - addTrade: (drug: Drugs, trade: TradeType) => void; - clearState: () => void; + addTrade: (drug: Drug, trade: TradeType) => void; + clearTradesAndOutcomes: () => void; + clearAll: () => void; } export const usePlayerStore = create((set) => ({ outcomes: [], + history: [], trades: new Map(), addOutcome: (outcome: Outcome) => - set((state) => ({ outcomes: [...state.outcomes, outcome] })), - addTrade: (drug: Drugs, trade: TradeType) => + set((state) => ({ + outcomes: [...state.outcomes, outcome], + history: [...state.history, outcome], + })), + addTrade: (drug: Drug, trade: TradeType) => set((state) => { const existingTrade = state.trades.get(drug); @@ -92,5 +70,10 @@ export const usePlayerStore = create((set) => ({ state.trades.set(drug, { quantity, direction }); return { trades: new Map(state.trades) }; }), - clearState: () => set({ trades: new Map(), outcomes: [] }), + clearTradesAndOutcomes: () => { + set({ trades: new Map(), outcomes: [] }); + }, + clearAll: () => { + set({ trades: new Map(), outcomes: [], history: [] }); + }, })); diff --git a/web/src/hooks/ui.tsx b/web/src/hooks/ui.tsx deleted file mode 100644 index 79a128c5e..000000000 --- a/web/src/hooks/ui.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import { create } from "zustand"; - -import { - Brooklyn, - CentralPark, - ConeyIsland, - Manhattan, - Queens, - Bronx, -} from "@/components/icons/locations"; - -import { - Ludes, - Weed, - Acid, - Speed, - Heroin, - Cocaine, -} from "@/components/icons/drugs"; - -import { Drugs, Locations, Outcome } from "./state"; -import React from "react"; - -export interface LocationProps { - name: Locations; - slug: string; - icon: React.FC; - id: string; -} - -export const locations: LocationProps[] = [ - { - name: Locations.Central, - slug: "central", - icon: CentralPark, - id: "0x43656e7472616c205061726b", - }, - { - name: Locations.Queens, - slug: "queens", - icon: Queens, - id: "0x517565656e73", - }, - { - name: Locations.Bronx, - slug: "bronx", - icon: Bronx, - id: "0x5468652042726f6e78", - }, - { - name: Locations.Jersey, - slug: "jersey", - icon: Manhattan, - id: "0x4a65727365792043697479", - }, - { - name: Locations.Coney, - slug: "coney", - icon: ConeyIsland, - id: "0x436f6e65792049736c616e64", - }, - { - name: Locations.Brooklyn, - slug: "brooklyn", - icon: Brooklyn, - id: "0x42726f6f6b6c796e", - }, -]; - -export interface DrugProps { - name: Drugs; - slug: string; - icon: React.FC; - id: string; -} - -const drugs: DrugProps[] = [ - { - name: Drugs.Ludes, - slug: "ludes", - icon: Ludes, - id: "0x4c75646573", - }, - { - name: Drugs.Speed, - slug: "speed", - icon: Speed, - id: "0x5370656564", - }, - { - name: Drugs.Weed, - slug: "weed", - icon: Weed, - id: "0x57656564", - }, - { - name: Drugs.Acid, - slug: "acid", - icon: Acid, - id: "0x41636964", - }, - { - name: Drugs.Heroin, - slug: "heroin", - icon: Heroin, - id: "0x4865726f696e", - }, - { - name: Drugs.Cocaine, - slug: "cocaine", - icon: Cocaine, - id: "0x436f6361696e65", - }, -]; - -export interface OutcomeProps { - outcome: Outcome; - name: string; - description: string; - imageSrc: string; -} - -export const outcomes: OutcomeProps[] = [ - { - outcome: Outcome.Paid, - name: "Paid Up", - description: "Smart move kid, now run along", - imageSrc: "/images/sunset.png", - }, - { - outcome: Outcome.Captured, - name: "Captured", - description: "Nothing personal kid", - imageSrc: "/images/events/smoking_gun.gif", - }, - { - outcome: Outcome.Escaped, - name: "Escaped", - description: "As soon as you saw the goons you split", - imageSrc: "/images/sunset.png", - }, -]; - -export interface UiState { - isConnected: boolean; - locations: LocationProps[]; - drugs: DrugProps[]; - outcomes: OutcomeProps[]; - getLocationBySlug: (slug: string) => LocationProps; - getLocationById: (id: string) => LocationProps; - getDrugBySlug: (slug: string) => DrugProps; - getDrugById: (name: string) => DrugProps; - getOutcome: (outcome: Outcome) => OutcomeProps; - isBackButtonVisible: (pathname: string) => Boolean; -} - -export const setIsConnected = (isConnected: boolean) => - useUiStore.setState((state) => ({ isConnected })); - -export const isBackButtonVisible = (pathname: string): Boolean => { - return ["/[game]/[locationSlug]/[drugSlug]"].includes(pathname); -}; - -export const getLocationBySlug = (slug: string): LocationProps => { - const { locations } = useUiStore.getState(); - const location = locations.find((i) => i.slug === slug); - return location || locations[0]; -}; - -export const getLocationById = (id: string): LocationProps => { - const { locations } = useUiStore.getState(); - const location = locations.find((i) => i.id === id); - return location || locations[0]; -}; - -export const getDrugBySlug = (slug: string): DrugProps => { - const { drugs } = useUiStore.getState(); - const drug = drugs.find((i) => i.slug === slug); - return drug || drugs[0]; -}; - -export const getDrugById = (id: string): DrugProps => { - const { drugs } = useUiStore.getState(); - const drug = drugs.find((i) => i.id === id); - return drug || drugs[0]; -}; - -export const getOutcome = (outcome: Outcome): OutcomeProps => { - const { outcomes } = useUiStore.getState(); - return outcomes.find((i) => i.outcome === outcome) || outcomes[0]; -}; - -export const useUiStore = create(() => ({ - isConnected: false, - locations, - drugs, - outcomes, - getLocationBySlug, - getLocationById, - getDrugBySlug, - getDrugById, - getOutcome, - isBackButtonVisible, -})); diff --git a/web/src/narrations.ts b/web/src/narrations.ts new file mode 100644 index 000000000..dd3662c50 --- /dev/null +++ b/web/src/narrations.ts @@ -0,0 +1,68 @@ +import { Outcome } from "./dojo/types"; + +type Encounter = "initial" | "repeat"; + +const Narrations: Record> = { + [Outcome.Paid]: { + initial: [ + "Smart move, handing over the cash. But know that we're always watching.", + "The streets have their own tax. Good on you for understanding.", + "Easiest way to keep your bones unbroken. Now get lost.", + "Blood or money, always easier when it's the latter. Move along.", + "We could've taken it by force, but you're wise to avoid the pain.", + ], + repeat: [ + "You again? It's like a subscription service with you. Convenient.", + "Your pockets seem to refill just for us. Do keep coming back.", + "Another day, another payout. Maybe consider a different route?", + "Consistency is key, huh? We appreciate your 'donations'.", + "Back so soon? At this rate, we should give you a loyalty card.", + ], + }, + [Outcome.Escaped]: { + initial: [ + "You might've outrun us this time, but the shadows talk. We'll find you.", + "Swift move, but luck only lasts so long. The streets don't forget.", + "Sprinting away won't save you forever. Next time, you won't see us coming.", + "Think you're a ghost? We have a knack for hunting phantoms.", + "Quick on your feet, huh? Makes the eventual catch even sweeter.", + ], + repeat: [ + "Again? Starting to admire your nerve, if not your wisdom.", + "You're getting good at this game, but remember, every chase ends eventually.", + "Lightning fast once more, but the storm is still coming.", + "Twice the escape, double the desire to get you next time.", + "Fancy footwork won't save you every time. Your luck is running thin.", + ], + }, + [Outcome.Captured]: { + initial: [ + "Thought you could outrun us? Now your cash and stash are ours.", + "Every step you took, we were right behind. Thanks for the bonus haul.", + "Nice try, but your pockets are a lot lighter now. Oh, and we'll be taking those drugs, too.", + "You might be fast, but not fast enough to keep your money or your stash.", + "Your escape plan had one flaw: leaving us your loot. Better luck next time.", + ], + repeat: [ + "Back again? Your pockets are like an ATM for us. Oh, and we'll help ourselves to those drugs again.", + "You're like a broken record: run, get caught, lose loot. When will you learn?", + "Twice in our net, and twice the profits. Thanks for the consistent supply, pal.", + "Starting to think you're doing this on purpose. Easy pickings for us either way.", + "Deja vu? For us, it's like hitting the jackpot every time. Your stash will do nicely.", + ], + }, +}; + +function getRandomIdx(length: number): number { + return Math.floor(Math.random() * length); +} + +// if first time, use initial response. repeat is a mix of initial and repeat +export function getNarration(outcome: Outcome, isInitial: boolean): string { + const encounterType = isInitial ? "initial" : "repeat"; + const lines = isInitial + ? Narrations[outcome][encounterType] + : [...Narrations[outcome].initial, ...Narrations[outcome].repeat]; + + return lines[getRandomIdx(lines.length)]; +} diff --git a/web/src/pages/[gameId]/[locationSlug]/[drugSlug].tsx b/web/src/pages/[gameId]/[locationSlug]/[drugSlug].tsx index 3de9334a8..15eadb616 100644 --- a/web/src/pages/[gameId]/[locationSlug]/[drugSlug].tsx +++ b/web/src/pages/[gameId]/[locationSlug]/[drugSlug].tsx @@ -5,7 +5,6 @@ import { useRouter } from "next/router"; import { Alert, ArrowEnclosed, Cart } from "@/components/icons"; import Image from "next/image"; import { Footer } from "@/components/Footer"; -import { DrugProps, getDrugBySlug, getLocationBySlug } from "@/hooks/ui"; import { Slider, SliderTrack, SliderFilledTrack } from "@chakra-ui/react"; @@ -16,16 +15,15 @@ import AlertMessage from "@/components/AlertMessage"; import { DrugMarket, useLocationEntity, -} from "@/hooks/dojo/entities/useLocationEntity"; -import { - PlayerEntity, - usePlayerEntity, -} from "@/hooks/dojo/entities/usePlayerEntity"; +} from "@/dojo/entities/useLocationEntity"; +import { PlayerEntity, usePlayerEntity } from "@/dojo/entities/usePlayerEntity"; import { formatQuantity, formatCash } from "@/utils/ui"; -import { useSystems } from "@/hooks/dojo/systems/useSystems"; +import { useSystems } from "@/dojo/systems/useSystems"; import { calculateMaxQuantity, calculateSlippage } from "@/utils/market"; import { useToast } from "@/hooks/toast"; -import { useDojo } from "@/hooks/dojo"; +import { getDrugBySlug, getLocationBySlug } from "@/dojo/helpers"; +import { DrugInfo } from "@/dojo/types"; +import { useDojo } from "@/dojo"; export default function Market() { const router = useRouter(); @@ -75,10 +73,12 @@ export default function Market() { setTradeDirection(index as TradeDirection); }; - const { buy, sell, isPending, error: txError } = useSystems(); + const [isSubmitting, setIsSubmitting] = useState(false); + const { buy, sell, error: txError } = useSystems(); const { addTrade } = usePlayerStore(); const onTrade = useCallback(async () => { + setIsSubmitting(true); playSound(Sounds.Trade); let toastMessage = "", hash = "", @@ -96,7 +96,7 @@ export default function Market() { toast(toastMessage, Cart, `http://amazing_explorer/${hash}`); - addTrade(drug.name, { + addTrade(drug.type, { direction: tradeDirection, quantity, } as TradeType); @@ -120,9 +120,11 @@ export default function Market() { return ( +
+ +
-
+ ); diff --git a/web/src/pages/[gameId]/event/decision.tsx b/web/src/pages/[gameId]/event/decision.tsx index 4763aa40a..a547498cc 100644 --- a/web/src/pages/[gameId]/event/decision.tsx +++ b/web/src/pages/[gameId]/event/decision.tsx @@ -1,17 +1,19 @@ import CrtEffect from "@/components/CrtEffect"; -import Header from "@/components/Header"; -import { useDojo } from "@/hooks/dojo"; -import { - PlayerStatus, - usePlayerEntity, -} from "@/hooks/dojo/entities/usePlayerEntity"; -import { useSystems } from "@/hooks/dojo/systems/useSystems"; -import { Action, usePlayerStore } from "@/hooks/state"; -import { getLocationById } from "@/hooks/ui"; -import { ConsequenceEventData } from "@/utils/event"; -import { Button, Heading, HStack, Image, Text, VStack } from "@chakra-ui/react"; +import Image from "next/image"; +import { useDojo } from "@/dojo"; +import { PlayerStatus, usePlayerEntity } from "@/dojo/entities/usePlayerEntity"; +import { getLocationById } from "@/dojo/helpers"; +import { useSystems } from "@/dojo/systems/useSystems"; +import { Action } from "@/dojo/types"; +import { usePlayerStore } from "@/hooks/state"; +import { ConsequenceEventData } from "@/dojo/events"; +import { Button, Heading, Text, VStack } from "@chakra-ui/react"; import { useRouter } from "next/router"; import { useCallback, useState } from "react"; +import Layout from "@/components/Layout"; +import { Footer } from "@/components/Footer"; + +const BASE_PAYMENT = 400; export default function Decision() { const router = useRouter(); @@ -36,10 +38,10 @@ export default function Decision() { router.replace(`/${gameId}/event/consequence?outcome=${event.outcome}`); }, - [gameId, nextLocation, router, decide], + [gameId, nextLocation, router, addOutcome, decide], ); - if (!playerEntity) { + if (!playerEntity || !router.isReady) { return <>; } @@ -51,16 +53,7 @@ export default function Decision() { return ( <> -
- + - muggers - - - - - + muggers + + + Better think fast... + + * They are demanding at least{" "} + {playerEntity.cash * 0.2 < BASE_PAYMENT ? "$400" : "20%"} of your + cash * + + +
+ + +
+
+ ); diff --git a/web/src/pages/[gameId]/index.tsx b/web/src/pages/[gameId]/index.tsx index 9d96aeea0..baa8b6ad4 100644 --- a/web/src/pages/[gameId]/index.tsx +++ b/web/src/pages/[gameId]/index.tsx @@ -16,9 +16,11 @@ export default function Join() { const router = useRouter(); return ( diff --git a/web/src/pages/[gameId]/travel.tsx b/web/src/pages/[gameId]/travel.tsx index 1a7623ce8..5bad2d1f7 100644 --- a/web/src/pages/[gameId]/travel.tsx +++ b/web/src/pages/[gameId]/travel.tsx @@ -10,36 +10,34 @@ import { useEventListener, Spacer, } from "@chakra-ui/react"; -import { Locations, usePlayerStore } from "@/hooks/state"; import { useRouter } from "next/router"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { generatePixelBorderPath } from "@/utils/ui"; import { Map } from "@/components/map"; import { motion } from "framer-motion"; -import { LocationProps, useUiStore, getLocationById } from "@/hooks/ui"; -import { useSystems } from "@/hooks/dojo/systems/useSystems"; -import { usePlayerEntity } from "@/hooks/dojo/entities/usePlayerEntity"; +import { useSystems } from "@/dojo/systems/useSystems"; +import { usePlayerEntity } from "@/dojo/entities/usePlayerEntity"; import { useToast } from "@/hooks/toast"; -import { useDojo } from "@/hooks/dojo"; -import { shortString } from "starknet"; +import { useDojo } from "@/dojo"; +import { getLocationById, getLocationByType, locations } from "@/dojo/helpers"; +import { LocationInfo } from "@/dojo/types"; export default function Travel() { const router = useRouter(); const gameId = router.query.gameId as string; const [targetId, setTargetId] = useState(""); const [currentLocationId, setCurrentLocationId] = useState(""); - const targetName = useMemo(() => getLocationById(targetId)?.name, [targetId]); - const { locations } = useUiStore.getState(); const { toast } = useToast(); const { account } = useDojo(); - const { travel, isPending, error: txError } = useSystems(); const { player: playerEntity } = usePlayerEntity({ gameId, address: account?.address, }); + const targetLocation = useMemo(() => getLocationById(targetId), [targetId]); + useEffect(() => { if (playerEntity) { const location = getLocationById(playerEntity.locationId); @@ -68,7 +66,7 @@ export default function Travel() { } else { setTargetId(locations[0].id); } - }, [targetId, locations]); + }, [targetId]); const back = useCallback(() => { const idx = locations.findIndex((location) => location.id === targetId); @@ -77,7 +75,7 @@ export default function Travel() { } else { setTargetId(locations[locations.length - 1].id); } - }, [targetId, locations]); + }, [targetId]); const onContinue = useCallback(async () => { if (targetId) { @@ -86,7 +84,7 @@ export default function Travel() { router.push(`/${gameId}/event/decision?nextId=${targetId}`); } else { toast( - `You've traveled to ${targetName}`, + `You've traveled to ${targetLocation.name}`, Car, `http://amazing_explorer/${hash}`, ); @@ -94,21 +92,24 @@ export default function Travel() { router.push(`/${gameId}/turn`); } } - }, [targetId, router, gameId, travel, toast]); + }, [targetId, router, gameId, targetLocation, travel, toast]); return ( { + setTargetId(getLocationByType(selected).id); + }} + /> + ), + }} + showMap={true} showBack={true} - map={ - { - setTargetId(shortString.encodeShortString(selected)); - }} - /> - } > @@ -131,7 +132,7 @@ export default function Travel() { > {targetId === currentLocationId ? "Current Location" - : `Travel to ${targetName}`} + : `Travel to ${targetLocation.name}`} - {targetName} + {targetLocation.name} {targetId === currentLocationId ? "Current Location" - : `Travel to ${targetName}`} + : `Travel to ${targetLocation.name}`} @@ -203,7 +204,7 @@ const Location = ({ selected: boolean; isCurrent: boolean; onClick: () => void; -} & LocationProps) => { +} & LocationInfo) => { const currentColor = isCurrent ? "yellow.400" : "neon.400"; return ( diff --git a/web/src/pages/[gameId]/turn.tsx b/web/src/pages/[gameId]/turn.tsx index cd2b60a99..7af5f48fc 100644 --- a/web/src/pages/[gameId]/turn.tsx +++ b/web/src/pages/[gameId]/turn.tsx @@ -1,14 +1,17 @@ import { Footer } from "@/components/Footer"; import { Bag, Event } from "@/components/icons"; import Layout from "@/components/Layout"; -import { useDojo } from "@/hooks/dojo"; -import { useGameEntity } from "@/hooks/dojo/entities/useGameEntity"; -import { usePlayerEntity } from "@/hooks/dojo/entities/usePlayerEntity"; +import { useDojo } from "@/dojo"; +import { useGameEntity } from "@/dojo/entities/useGameEntity"; +import { usePlayerEntity } from "@/dojo/entities/usePlayerEntity"; +import { + getDrugByType, + getLocationById, + getOutcomeByType, +} from "@/dojo/helpers"; import { TradeDirection, usePlayerStore } from "@/hooks/state"; -import { getDrugById, getOutcome, getLocationById } from "@/hooks/ui"; import { - Box, Button, HStack, ListItem, @@ -30,17 +33,21 @@ export default function Turn() { gameId, }); - const { trades, outcomes, clearState } = usePlayerStore(); + const { trades, outcomes, clearTradesAndOutcomes } = usePlayerStore(); if (!playerEntity || !gameEntty) { return <>; } + const locationInfo = getLocationById(playerEntity.locationId); + return ( {trades.size > 0 && ( @@ -56,11 +63,12 @@ export default function Turn() { {Array.from(trades).map(([drug, trade]) => { const change = trade.direction === TradeDirection.Buy ? "+" : "-"; + const drugInfo = getDrugByType(drug); return ( @@ -77,23 +85,26 @@ export default function Turn() { - {getLocationById(playerEntity.locationId).icon({})} - {getLocationById(playerEntity.locationId).name} + {locationInfo.icon({})} + {locationInfo.name} - {outcomes.map((outcome, index) => ( - - - - - {getOutcome(outcome).name} + {outcomes.map((outcome, index) => { + const outcomeInfo = getOutcomeByType(outcome); + return ( + + + + + {outcomeInfo.name} + + + {} + - - {getOutcome(outcome).description} - - - - ))} + + ); + })} ; @@ -101,10 +112,8 @@ export default function Turn() {