Skip to content

Commit

Permalink
💄 Improve concert scheduling flow
Browse files Browse the repository at this point in the history
  • Loading branch information
sleepyfran committed Jan 16, 2024
1 parent 64954ee commit 83d363f
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 22 deletions.
48 changes: 48 additions & 0 deletions src/Duets.Cli/Components/ConcertDetails.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[<AutoOpen>]
module Duets.Cli.Components.ConcertDetails

open Duets.Agents
open Duets.Cli.Text
open Duets.Common
open Duets.Entities
open Duets.Simulation
open Spectre.Console

/// Shows a social network post posted by the given account.
let showConcertDetails (concert: Concert) =
let state = State.get ()
let place = Queries.World.placeInCityById concert.CityId concert.VenueId
let cityName = Generic.cityName concert.CityId
let band = Queries.Bands.currentBand state

let title =
match concert.ParticipationType with
| Headliner -> band.Name |> Styles.title
| OpeningAct(headlinerId, _) ->
let headliner = Queries.Bands.byId state headlinerId
$"{headliner.Name |> Styles.title}, support: {band.Name |> Styles.faded}"

lineBreak ()

Panel(
Rows(
Markup(title),
Markup($"Venue: {place.Name |> Styles.place}, {cityName}"),
Markup(
$"Scheduled for {concert.Date |> Generic.dateWithDay} at {concert.DayMoment |> Generic.dayMomentName |> String.lowercase}"
)
),
Header =
PanelHeader($"{Emoji.concert} Concert scheduled" |> Styles.header),
Border = BoxBorder.Rounded,
Expand = true
)
|> AnsiConsole.Write

showTips
[ "Make sure to be at the venue at least 2 day moments before the concert so that you can set up the merch stand and do a sound check."
"You can order merchandise from the Merchandise Workshop to sell at your concerts. They take a week to deliver, so make sure you order them in time!" ]

lineBreak ()

showContinuationPrompt ()
4 changes: 1 addition & 3 deletions src/Duets.Cli/Components/Effect.fs
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,7 @@ let private displayEffect effect =
|> Styles.warning
|> showMessage)
| ConcertScheduled(_, ScheduledConcert(concert, _)) ->
let place = Queries.World.placeInCityById concert.CityId concert.VenueId

Phone.concertAssistantAppTicketDone place concert |> showMessage
showConcertDetails concert
| ConcertFinished(_, pastConcert, income) ->
let concert = Concert.fromPast pastConcert

Expand Down
5 changes: 5 additions & 0 deletions src/Duets.Cli/Components/Tip.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ let showTip title text =
Expand = false
)
|> AnsiConsole.Write

/// Shows a list of tips (hints) to the player as a list with a header.
let showTips tips =
"Tips" |> Styles.title |> showMessage
tips |> List.iter (fun tip -> $"- {tip}" |> Styles.hint |> showMessage)
1 change: 1 addition & 0 deletions src/Duets.Cli/Duets.Cli.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
<Compile Include="Components\Prompt.fs"/>
<Compile Include="Components\Review.fs"/>
<Compile Include="Components\Tip.fs" />
<Compile Include="Components\ConcertDetails.fs" />
<Compile Include="Components\Effect.fs"/>
<Compile Include="Components\Commands\Command.fs"/>
<Compile Include="Components\Commands\Exit.Command.fs"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,18 @@ and private promptForCity app date dayMoment =
| None -> app ()

and private promptForVenue app date dayMoment city =
let band = Queries.Bands.currentBand (State.get ())

let _, maxCapacityRecommended =
Queries.Concerts.suitableVenueCapacity (State.get ()) band.Id

let venues =
Queries.World.placeIdsByTypeInCity city.Id PlaceTypeIndex.ConcertSpace
|> List.map (Queries.World.placeInCityById city.Id)

let selectedVenue =
showOptionalChoicePrompt
Phone.concertAssistantAppShowVenuePrompt
(Phone.concertAssistantAppShowVenuePrompt maxCapacityRecommended)
Generic.cancel
(fun (place: Place) ->
match place.PlaceType with
Expand All @@ -97,8 +102,15 @@ and private promptForVenue app date dayMoment city =
| None -> app ()

and private promptForTicketPrice app date dayMoment city venueId =
let state = State.get ()
let band = Queries.Bands.currentBand state

let recommendedPrice =
Queries.Concerts.fairTicketPrice state band.Id |> Amount.fromDecimal

let ticketPrice =
showDecimalPrompt Phone.concertAssistantAppTicketPricePrompt
Phone.concertAssistantAppTicketPricePrompt recommendedPrice
|> showDecimalPrompt

Concert.validatePrice ticketPrice
|> Result.switch
Expand Down
10 changes: 3 additions & 7 deletions src/Duets.Cli/Text/Phone.fs
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,11 @@ let concertAssistantAppShowTimePrompt = "At what time?"

let concertAssistantAppShowCityPrompt = "In which city?"

let concertAssistantAppShowVenuePrompt = "In which venue?"
let concertAssistantAppShowVenuePrompt maxCapacity =
$"In which venue? (Recommended capacity: up to {maxCapacity})"

let concertAssistantAppTicketPricePrompt recommendedPrice =
$"""What will the price of each ticket be? (Recommended: {recommendedPrice |> Styles.money})
{Styles.danger
"Keep in mind that putting high prices might affect how many people will go"}"""
$"""What will the price of each ticket be? (Recommended: up to {recommendedPrice |> Styles.money})"""

let concertAssistantAppDateAlreadyBooked date =
Styles.error $"You already have a concert on {Date.simple date}!"
Expand All @@ -140,9 +139,6 @@ let concertAssistantAppTicketPriceTooHigh price =
Styles.error
$"{Styles.decimal price} is a bit too high for a concert. Maybe a bit less?"

let concertAssistantAppTicketDone (place: Place) concert =
$"""Done! You scheduled a concert in {Styles.place place.Name} on {Styles.highlight (Date.simple concert.Date)}. Be sure to be in the place at the moment of the concert, {Styles.danger "otherwise it'd fail miserably!"}"""

(* --- Statistics --- *)

let statisticsAppTitle = "Statistics"
Expand Down
3 changes: 3 additions & 0 deletions src/Duets.Cli/Text/Styles.fs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ let information text = $"[underline]{text}[/]"
/// Pre-defined style for showing text that does not draw attention immediately.
let faded text = $"[grey]{text}[/]"

/// Pre-defined style for showing text that is a hint to the user.
let hint = faded

/// Pre-defined style for showing text that draws attention.
let highlight text = $"[bold deepskyblue3]{text}[/]"

Expand Down
12 changes: 3 additions & 9 deletions src/Duets.Simulation/Concerts/OpeningActOpportunities.fs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ let private generateOpeningActShowsOnDate state headlinerBands cityId date =

let earningPercentage = calculateEarningPercentage headlinerFameLevel

let venue = findSuitableVenue venuesInCity headliner.Fans
let venue = findSuitableVenue state venuesInCity headliner

let concert =
Concert.create
Expand All @@ -59,14 +59,8 @@ let private generateOpeningActShowsOnDate state headlinerBands cityId date =

(headliner, concert))

let private findSuitableVenue venuesInCity fans : Place =
let range =
match fans with
| fans when fans <= 1000 -> (0, 300)
| fans when fans <= 5000 -> (0, 500)
| fans when fans <= 20000 -> (500, 5000)
| fans when fans <= 100000 -> (500, 20000)
| _ -> (500, 500000)
let private findSuitableVenue state venuesInCity band : Place =
let range = Queries.Concerts.suitableVenueCapacity state band.Id

(*
We rely on the fact that there will always be a suitable venue in the city.
Expand Down
14 changes: 13 additions & 1 deletion src/Duets.Simulation/Queries/Concerts.fs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ let scheduledAroundDate state bandId =

let aroundCurrentDate concert =
let spanBetween = concert.Date - today
if abs(spanBetween.Days) <= 1 then Some concert else None
if abs (spanBetween.Days) <= 1 then Some concert else None

let concertsScheduledAroundCurrentDate =
timeline.ScheduledEvents
Expand Down Expand Up @@ -135,6 +135,18 @@ let fairTicketPrice state bandId =
| fame when fame < 80 -> 75.0m
| _ -> 100.0m

/// Returns the range of capacity that the venue needs to have to be suitable
/// for the given band.
let suitableVenueCapacity state bandId =
let band = Bands.byId state bandId

match band.Fans with
| fans when fans <= 1000 -> (0, 300)
| fans when fans <= 5000 -> (0, 500)
| fans when fans <= 20000 -> (500, 5000)
| fans when fans <= 100000 -> (500, 20000)
| _ -> (500, 500000)

/// Calculates the percentage off the tickets that the concert space will take
/// based on the quality of the place and the capacity.
let concertSpaceTicketPercentage (place: Place) =
Expand Down

0 comments on commit 83d363f

Please sign in to comment.