├── .gitignore ├── README.md ├── screenshots ├── card-bad.png ├── card-choice.gif ├── card-good.png ├── card-special.png ├── current-task.png ├── gameover.png ├── karma.png ├── map-high.png ├── map-low.png ├── metrics.png ├── restartingtutorial.gif ├── shiny-decisions-large.png └── startscreen.png ├── shiny.decisions.Rproj └── src ├── app.R └── app ├── data └── options.xlsx ├── global.R ├── google-analytics.html ├── logic ├── dataManager.R ├── deckManager.R ├── gameManager.R ├── mapManager.R ├── metricsManager.R └── stateManager.R ├── server.R ├── styles ├── main.scss ├── modules │ ├── _animations.scss │ ├── _config.scss │ └── _static.scss └── partials │ ├── _cards.scss │ ├── _layout.scss │ ├── _map.scss │ ├── _metrics.scss │ └── _modals.scss ├── ui.R └── www ├── assets ├── cards │ ├── beekeeper-1.png │ ├── blacksmith-1.png │ ├── blank.png │ ├── boy-1.png │ ├── boy-2.png │ ├── boy-3.png │ ├── boy-4.png │ ├── boy-5.png │ ├── boy-6.png │ ├── boy-7.png │ ├── chef-1.png │ ├── doctor-1.png │ ├── farmer-1.png │ ├── girl-1.png │ ├── girl-2.png │ ├── girl-3.png │ ├── girl-4.png │ ├── hunter-1.png │ ├── man-1.png │ ├── man-10.png │ ├── man-11.png │ ├── man-12.png │ ├── man-13.png │ ├── man-14.png │ ├── man-15.png │ ├── man-16.png │ ├── man-17.png │ ├── man-18.png │ ├── man-19.png │ ├── man-2.png │ ├── man-20.png │ ├── man-21.png │ ├── man-22.png │ ├── man-23.png │ ├── man-24.png │ ├── man-25.png │ ├── man-26.png │ ├── man-27.png │ ├── man-28.png │ ├── man-29.png │ ├── man-3.png │ ├── man-30.png │ ├── man-31.png │ ├── man-32.png │ ├── man-33.png │ ├── man-34.png │ ├── man-35.png │ ├── man-36.png │ ├── man-37.png │ ├── man-4.png │ ├── man-5.png │ ├── man-6.png │ ├── man-7.png │ ├── man-8.png │ ├── man-9.png │ ├── nun-1.png │ ├── pirate-1.png │ ├── pirate-2.png │ ├── sailor-1.png │ ├── santa-1.png │ ├── scientist-1.png │ ├── shady-1.png │ ├── shady-2.png │ ├── shady-3.png │ ├── woman-1.png │ ├── woman-10.png │ ├── woman-11.png │ ├── woman-12.png │ ├── woman-13.png │ ├── woman-14.png │ ├── woman-16.png │ ├── woman-17.png │ ├── woman-18.png │ ├── woman-19.png │ ├── woman-2.png │ ├── woman-20.png │ ├── woman-21.png │ ├── woman-22.png │ ├── woman-23.png │ ├── woman-24.png │ ├── woman-25.png │ ├── woman-26.png │ ├── woman-6.png │ ├── woman-7.png │ ├── woman-8.png │ └── woman-9.png ├── map │ ├── cold.png │ ├── fire.png │ ├── house.png │ ├── house_broken.png │ ├── mad.png │ ├── mask.png │ ├── office.png │ ├── sick.png │ ├── smile.png │ ├── stareyes.png │ ├── thunder.png │ ├── tornado.png │ ├── tree.png │ ├── tree_large.png │ └── tree_small.png └── ui │ ├── author.png │ ├── icons │ ├── evil.png │ ├── friend.png │ ├── gold.png │ ├── halo.png │ └── salad.png │ └── pause.png ├── scripts ├── analytics-events.js ├── card_stack.js ├── filter-toggler.js ├── hammer.min.js └── update_map_style.js └── styles └── sass.min.css /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | .httr-oauth 6 | rsconnect 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shiny.decisions 2 | A game about making the best of terrible choices 3 | 4 | In shiny decisions your goal is to last as long as possible while making decisions that affect the wealth, population and environment quality in the world. 5 | 6 | If any of those metrics reaches zero, its game over! How long can you last? 7 | 8 | Live version: https://sparktuga.shinyapps.io/ShinyDecisions/ 9 | 10 | ![Screenshot](screenshots/shiny-decisions-large.png "Game screen") 11 | 12 | --- 13 | 14 | # Installation 15 | Some of the packages included in this project are currently only available on github. This includes `shiny.grid` and `shiny.blank`. To install them locally remember to run on your machine the following commands: 16 | 17 | ``` 18 | library(devtools) 19 | install.packages(devtools) 20 | devtools::install_github('pedrocoutinhosilva/shiny.grid') 21 | devtools::install_github('pedrocoutinhosilva/shiny.blank') 22 | ``` 23 | 24 | --- 25 | 26 | # Starting a game 27 | From the start game screen you will be able to start a new in any difficulty (Easy, medium or hard). 28 | ![Game start](screenshots/startscreen.png "Game start") 29 | 30 | You can also toggle the tutorial on and of if you already know the basics of the game. 31 | ![Tutorial](screenshots/restartingtutorial.gif "Tutorial") 32 | 33 | --- 34 | 35 | # During the game 36 | The game UI has 3 main areas: 37 | 38 | #### Metrics 39 | Displays the current value of the different metrics you need to manage. Those metrics include: 40 | - **Karma:** Being too bad or too good wont lose you the game, but might have some hidden effects... 41 | - **Wealth:** Things cost money. No money no fun :/ 42 | - **Public opinion:** If no one likes you, you probably wont last long in power! 43 | - **Environment:** If the world is beyond saving, whats the point? 44 | 45 | The game metrics: 46 | ![Metrics](screenshots/metrics.png "Metrics") 47 | 48 | The karma meter: 49 | ![karma](screenshots/karma.png "karma") 50 | 51 | #### Map 52 | A visual representation of what effects your actions are having in the world. Icons and the map color with change depending on how high or low some metrics are. 53 | 54 | Low metrics: 55 | ![map-low](screenshots/map-low.png "map-low") 56 | 57 | High metrics: 58 | ![map-high](screenshots/map-high.png "map-high") 59 | 60 | 61 | #### current task 62 | Your next task and the way you can move forward in the game. Above the task you can also see the current week in game. Every task you complete will move the game forward 1 week. 63 | ![current-task](screenshots/current-task.png "current-task") 64 | 65 | Card colors reflect the card pool and severity of the current card. The darker the card the more severe the task is, and more change it will have on the metrics. Currently 3 cards pools are implemented: 66 | 67 | Bad cards (Cards that will have mostly negative consequences, red background): 68 | ![card-bad](screenshots/card-bad.png "card-bad") 69 | 70 | Good cards (Cards that will have mostly good consequences, green background): 71 | ![card-good](screenshots/card-good.png "card-good") 72 | 73 | Special cards (Cards that will have unique consequences, purple background): 74 | ![card-special](screenshots/card-special.png "card-special") 75 | 76 | If you want to know more about all the possible cards and options available, you can check the google spreadsheet containing all possibilities at: 77 | https://docs.google.com/spreadsheets/d/1LwIPKAxbKvuGyMKktcTVuYZbTda0WMQxmNMhxqaQhGg 78 | 79 | #### Card stack 80 | Each task as 2 possible decisions, left or right. Simply drag and hold the card to each side to see what decisions you can take. while holding the card the names of the metrics that will be affected by that decision will also blink. Drag and release to commit to your decision. 81 | ![card-choice](screenshots/card-choice.gif "card-choice") 82 | 83 | #### Game end 84 | when one of the metrics reaches zero, the game is over and you will be taken to the end game screen. Here you can see how many weeks you lasted, as well as return to the initial screen in case you want to go again. 85 | ![gameover](screenshots/gameover.png "gameover") 86 | 87 | --- 88 | 89 | # Code Structure 90 | Shiny.decisions takes full advantage of modules and R6 classes for managing the code complexity. 91 | Most of the project is broken down into R6 managers that take care of specific parts of the workflow: 92 | 93 | ### Game Manager 94 | The brain being the game flow. It manages all game dependencies and is responsible for high level actions that drive the state of the game. 95 | 96 | ### Data Manager 97 | Connects and exposes all external data required by the app. This includes the main data storage regarding task cards, game options and game modes loaded from a google spreadsheet. 98 | 99 | Most options and cards can be added or tweaked without code changes directly on the spreadsheets. 100 | 101 | The current live spreadsheet can be found at: 102 | https://docs.google.com/spreadsheets/d/1LwIPKAxbKvuGyMKktcTVuYZbTda0WMQxmNMhxqaQhGg 103 | 104 | ### State Manager 105 | Stores information regarding the current state of the game. This includes the current value of the different metrics, map markers and state checks for specific game phases. 106 | 107 | ### Metrics Manager 108 | Responsible for displaying the current state metrics on the UI. 109 | 110 | ### Map Manager 111 | Responsible for displaying map information according to the the current state metrics on the UI. 112 | 113 | ### Deck Manager 114 | Responsible for creating cards according to the game state and switching between card pools depending on the current game phase. 115 | 116 | --- 117 | 118 | # Future improvements 119 | A lot of ideas are still floating around on how to improve this application: 120 | 121 | - Leader board; 122 | - More cards!; 123 | - Special game modes with different decks and objectives; 124 | - Map interactions; 125 | - Power up modifiers; 126 | 127 | Got any more ideas, questions or feedback? Feel free to get in touch! 128 | - Twitter: https://twitter.com/sparktuga 129 | - linkedin: https://www.linkedin.com/in/pedrocoutinhosilva 130 | 131 | --- 132 | 133 | # Links 134 | #### Useful references 135 | - Modules: https://shiny.rstudio.com/articles/modules.html 136 | - R6 classes: https://adv-r.hadley.nz/r6.html 137 | - SASS: https://github.com/rstudio/sass 138 | 139 | #### Assets 140 | - Images: https://opengameart.org/ 141 | - NES.css framework: https://nostalgic-css.github.io/NES.css/ 142 | 143 | #### Dependencies 144 | - Shiny.grid: https://github.com/pedrocoutinhosilva/shiny.grid 145 | - Shiny.blank: https://github.com/pedrocoutinhosilva/shiny.blank 146 | - Hammer.js: https://hammerjs.github.io/ 147 | - Swipe cards: https://github.com/simonepm/likecarousel 148 | - Leaflet for R: https://rstudio.github.io/leaflet/ 149 | 150 | #### Data 151 | - Google spreadsheet database: https://docs.google.com/spreadsheets/d/1LwIPKAxbKvuGyMKktcTVuYZbTda0WMQxmNMhxqaQhGg 152 | - World cities dataset: https://simplemaps.com/data/world-cities 153 | - World capitals dataset: https://www.kaggle.com/nikitagrec/world-capitals-gps 154 | -------------------------------------------------------------------------------- /screenshots/card-bad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/card-bad.png -------------------------------------------------------------------------------- /screenshots/card-choice.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/card-choice.gif -------------------------------------------------------------------------------- /screenshots/card-good.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/card-good.png -------------------------------------------------------------------------------- /screenshots/card-special.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/card-special.png -------------------------------------------------------------------------------- /screenshots/current-task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/current-task.png -------------------------------------------------------------------------------- /screenshots/gameover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/gameover.png -------------------------------------------------------------------------------- /screenshots/karma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/karma.png -------------------------------------------------------------------------------- /screenshots/map-high.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/map-high.png -------------------------------------------------------------------------------- /screenshots/map-low.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/map-low.png -------------------------------------------------------------------------------- /screenshots/metrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/metrics.png -------------------------------------------------------------------------------- /screenshots/restartingtutorial.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/restartingtutorial.gif -------------------------------------------------------------------------------- /screenshots/shiny-decisions-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/shiny-decisions-large.png -------------------------------------------------------------------------------- /screenshots/startscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/screenshots/startscreen.png -------------------------------------------------------------------------------- /shiny.decisions.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | -------------------------------------------------------------------------------- /src/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | 3 | shinyAppDir("app") 4 | -------------------------------------------------------------------------------- /src/app/data/options.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/data/options.xlsx -------------------------------------------------------------------------------- /src/app/global.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(shiny.grid) 3 | library(shiny.blank) 4 | library(htmltools) 5 | library(tidyr) 6 | library(leaflet) 7 | library(R6) 8 | library(googlesheets) 9 | library(glue) 10 | library(utils) 11 | library(dplyr) 12 | library(jsonlite) 13 | library(modules) 14 | library(sass) 15 | library(readxl) 16 | 17 | # Remember to install github packages if you dont have them already. 18 | # install.packages('devtools') 19 | # library(devtools) 20 | # devtools::install_github('pedrocoutinhosilva/shiny.grid') 21 | # devtools::install_github('pedrocoutinhosilva/shiny.blank') 22 | 23 | # Process and minify styles 24 | sass( 25 | sass::sass_file("styles/main.scss"), 26 | options = sass_options(output_style = "compressed"), 27 | output = "www/styles/sass.min.css" 28 | ) 29 | 30 | # Generic gameManager to initialize ui elements 31 | gameManager <- use("logic/gameManager.R")$gameManager$new() 32 | -------------------------------------------------------------------------------- /src/app/google-analytics.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /src/app/logic/dataManager.R: -------------------------------------------------------------------------------- 1 | import("R6") 2 | import("googlesheets") 3 | import("tidyr") 4 | import("glue") 5 | 6 | export("DataManager") 7 | 8 | # Deals with external data 9 | DataManager <- R6Class("DataManager", 10 | private = list( 11 | data = NULL, 12 | cities = NULL, 13 | options = NULL, 14 | settings = NULL, 15 | 16 | cards = list(), 17 | decks = list(), 18 | 19 | cardTypes = c("Tutorial", "Bad", "Good", "Special", "Death") 20 | ), 21 | 22 | public = list( 23 | initialize = function(sheetId, force_update = FALSE) { 24 | if (force_update) { 25 | print("Updating from online data") 26 | 27 | sheet_metadata <- googlesheets::gs_url( 28 | glue::glue("https://docs.google.com/spreadsheets/d/{sheetId}"), 29 | visibility = "public", 30 | lookup = FALSE 31 | ) 32 | gs_download(sheet_metadata, to = "data/options.xlsx", overwrite = TRUE) 33 | } 34 | 35 | private$settings <- readxl::read_xlsx("data/options.xlsx", "Game Settings") 36 | private$decks <- readxl::read_xlsx("data/options.xlsx", "Decks") 37 | private$options <- readxl::read_xlsx("data/options.xlsx", "Options") 38 | private$cities <- readxl::read_xlsx("data/options.xlsx", "Map Cities") 39 | 40 | lapply(private$cardTypes, function(type) { 41 | private$cards[[type]] <- readxl::read_xlsx("data/options.xlsx", sheet = type) 42 | }) 43 | }, 44 | 45 | getCities = function() { 46 | return(private$cities) 47 | }, 48 | 49 | getSettings = function(gameType) { 50 | return(private$settings[which(private$settings$`Game Type` == gameType), ]) 51 | }, 52 | 53 | getOptions = function(attribute) { 54 | return(drop_na(private$options[attribute])) 55 | }, 56 | 57 | getDeckOptions = function(name) { 58 | decks <- private$decks 59 | return(decks[which(decks$`Deck Name` == name), ]) 60 | }, 61 | 62 | getCards = function() { 63 | return(private$cards) 64 | } 65 | ) 66 | ) 67 | -------------------------------------------------------------------------------- /src/app/logic/deckManager.R: -------------------------------------------------------------------------------- 1 | import("R6") 2 | import("utils") 3 | import("jsonlite") 4 | import("glue") 5 | import("dplyr") 6 | import("utils") 7 | import("htmltools") 8 | 9 | export("deckManager") 10 | 11 | cleanCardMessage <- function(string) { 12 | stringr::str_replace_all( 13 | string, 14 | c("\\{" = "{`", "\\}" = "`}") 15 | ) 16 | } 17 | 18 | ui <- function(inputId = "card_stack") { 19 | tagList( 20 | div( 21 | id = glue::glue("{inputId}_wrapper"), 22 | div(id = inputId) 23 | ), 24 | tags$script(src = "scripts/hammer.min.js"), 25 | tags$script(src = "scripts/card_stack.js") 26 | ) 27 | } 28 | 29 | deckManager <- R6Class("deckManager", 30 | private = list( 31 | gameSettings = NULL, 32 | dataManager = NULL, 33 | stateManager = NULL, 34 | 35 | gameFlow = NULL, 36 | currentDeck = NULL, 37 | 38 | getCardType = function(pool, prob, saving_grace) { 39 | 40 | # Get random card type 41 | cardType <- sample( 42 | pool, 43 | size = 1, 44 | prob = prob, 45 | replace = TRUE 46 | ) 47 | 48 | if (!(cardType %in% c("Tutorial", "Death"))) { 49 | # roll aditional checks based on karma 50 | # If karma is low, double roll for bad cards based on how low 51 | if (saving_grace < 50 && cardType != "Bad") { 52 | save_roll <- sample( c(1:1000), size = 1) 53 | # if the save roll fails, card type is automatically set to bad 54 | if (save_roll < 100 - saving_grace) { 55 | cardType <- "Bad" 56 | } 57 | } 58 | # If karma is high, double roll for good kards based on how high 59 | if (saving_grace > 50 && cardType == "Bad") { 60 | grace_roll <- sample( c(1:1000), size = 1) 61 | # if the grace roll passes, card type is automatically set to good 62 | if (grace_roll < saving_grace) { 63 | cardType <- "Good" 64 | } 65 | } 66 | } 67 | 68 | return(cardType) 69 | }, 70 | 71 | generateTemplateCard = function() { 72 | deckOptions <- private$gameFlow[[private$currentDeck]] 73 | 74 | cardType <- private$getCardType( 75 | strsplit(deckOptions$`Card Pool`, ", ")[[1]], 76 | strsplit(deckOptions$`Pool Weight`, ", ")[[1]], 77 | private$stateManager$state$karma 78 | ) 79 | 80 | if (private$gameFlow[[private$currentDeck]]$`Order Fixed`) { 81 | deckLimit <- nrow(private$dataManager$getCards()[[cardType]]) 82 | deckSize <- as.numeric(private$gameFlow[[private$currentDeck]]$`Deck Size`) 83 | currentCardRow <- deckLimit - deckSize 84 | cardTemplate <- private$dataManager$getCards()[[cardType]][currentCardRow, ] 85 | } else { 86 | cardTemplate <- sample_n(private$dataManager$getCards()[[cardType]], 1) 87 | } 88 | 89 | state_min_intensity <- max(deckOptions$`Min Intensity`, cardTemplate$`Min Intensity`) 90 | state_max_intensity <- min(deckOptions$`Max Intensity`, cardTemplate$`Max Intensity`) 91 | 92 | if(state_min_intensity == state_max_intensity) 93 | state_max_intensity <- state_max_intensity + 1 94 | 95 | # Get random intensity level 96 | intensity_level <- sample( 97 | c(state_min_intensity:state_max_intensity), 98 | size = 1, 99 | prob = c(state_max_intensity:state_min_intensity), 100 | replace = TRUE 101 | ) 102 | intensityMultiplier <- 1 + ((intensity_level - 1) / 20) 103 | 104 | # Generate random options 105 | options <- vector("list", length(names(private$dataManager$getOptions()))) 106 | names(options) <- names(private$dataManager$getOptions()) 107 | for ( option in names(options)){ 108 | options[option] <- sample_n(private$dataManager$getOptions(option), 1) 109 | } 110 | 111 | options <- modifyList(options, list( 112 | `Danger Level` = private$dataManager$getOptions("Danger Level")[intensity_level, ], 113 | `Prosperity Level` = private$dataManager$getOptions("Prosperity Level")[intensity_level, ] 114 | )) 115 | 116 | color_scale <- private$dataManager$getOptions( 117 | as.character(glue::glue("{cardType} Color Scale")) 118 | ) 119 | 120 | background_colors <- list( 121 | left = as.character(color_scale[((intensity_level * 2) - 1), ]), 122 | right = as.character(color_scale[(intensity_level * 2), ]) 123 | ) 124 | 125 | background_image <- ifelse ( 126 | cardTemplate$`Image` == "random", 127 | paste0(sample(list.files('www/assets/cards'), 1)), 128 | glue::glue("{cardTemplate$`Image`}.png") 129 | ) 130 | 131 | # Fills in any potential glue string options 132 | options <- as.list(sapply(names(options), function(option) { 133 | do.call(glue::glue, modifyList(list(cleanCardMessage(options[[option]])), options)) 134 | }, USE.NAMES = TRUE)) 135 | 136 | left_values = list( 137 | message = do.call( 138 | glue::glue, 139 | modifyList(list(cleanCardMessage(cardTemplate$`Left Message`)), options) 140 | ), 141 | delta = list( 142 | karma = cardTemplate$`Left Karma` * intensityMultiplier, 143 | wealth = cardTemplate$`Left Wealth` * intensityMultiplier, 144 | opinion = cardTemplate$`Left Opinion` * intensityMultiplier, 145 | environment = cardTemplate$`Left Environment` * intensityMultiplier 146 | ) 147 | ) 148 | right_values = list( 149 | message = do.call( 150 | glue::glue, 151 | modifyList(list(cleanCardMessage(cardTemplate$`Right Message`)), options) 152 | ), 153 | delta = list( 154 | karma = cardTemplate$`Right Karma` * intensityMultiplier, 155 | wealth = cardTemplate$`Right Wealth` * intensityMultiplier, 156 | opinion = cardTemplate$`Right Opinion` * intensityMultiplier, 157 | environment = cardTemplate$`Right Environment` * intensityMultiplier 158 | ) 159 | ) 160 | 161 | # To spice things up, randomly switches left and right values half of the time 162 | if (!(cardType %in% c("Tutorial", "Death"))) { 163 | if(sample( c(TRUE, FALSE), size = 1)) { 164 | temp <- left_values 165 | left_values <- right_values 166 | right_values <- temp 167 | } 168 | } 169 | 170 | card <- list( 171 | background = list( 172 | image = glue::glue("assets/cards/{background_image}"), 173 | color_left = background_colors$left, 174 | color_right = background_colors$right 175 | ), 176 | message = list ( 177 | task = do.call( 178 | glue::glue, 179 | modifyList(list(cleanCardMessage(cardTemplate$`Template`)), options) 180 | ), 181 | left = left_values$message, 182 | right = right_values$message, 183 | week = switch( 184 | cardType, 185 | "Tutorial" = list( 186 | text = "Tutorial", 187 | increment = 0 188 | ), 189 | "Death" = list( 190 | text = "Afterlife", 191 | increment = 0 192 | ), 193 | list( 194 | text = paste0("Week ", private$stateManager$state$week), 195 | increment = 1 196 | ) 197 | ) 198 | ), 199 | delta = list( 200 | left = left_values$delta, 201 | right = right_values$delta 202 | ) 203 | ) 204 | 205 | return (card) 206 | } 207 | ), 208 | 209 | public = list( 210 | ui = ui, 211 | getCurrentDeck = function() { private$currentDeck }, 212 | 213 | triggerDeathPhase = function() { 214 | private$currentDeck = "Death" 215 | }, 216 | 217 | resetState = function(gameType = "Medium", 218 | skipTutorial = FALSE, 219 | dataManager, 220 | stateManager) { 221 | private$dataManager <- dataManager 222 | private$stateManager <- stateManager 223 | 224 | private$gameSettings <- private$dataManager$getSettings(gameType) 225 | 226 | private$gameFlow <- list() 227 | specialDecks <- strsplit(private$gameSettings$`Special Decks`, ", ")[[1]] 228 | gameTypeDecks <- strsplit(private$gameSettings$`Fixed Decks`, ", ")[[1]] 229 | gameDeckSizes <- strsplit(private$gameSettings$`Deck Sizes`, ", ")[[1]] 230 | 231 | lapply(gameTypeDecks, function(deckName) { 232 | options <- private$dataManager$getDeckOptions(deckName) 233 | 234 | deckIndex <- which(gameTypeDecks == deckName)[1] 235 | nextDeckIndex <- deckIndex + 1 236 | if(nextDeckIndex > length(gameTypeDecks)) nextDeckIndex <- length(gameTypeDecks) 237 | options$`Next Deck` <- gameTypeDecks[[nextDeckIndex]] 238 | options$`Deck Size` <- gameDeckSizes[[deckIndex]] 239 | private$gameFlow[[deckName]] <- options 240 | }) 241 | 242 | lapply(specialDecks, function(deckName) { 243 | options <- private$dataManager$getDeckOptions(deckName) 244 | 245 | options$`Next Deck` <- deckName 246 | options$`Deck Size` <- nrow(private$dataManager$getCards()[[deckName]]) 247 | 248 | private$gameFlow[[deckName]] <- options 249 | }) 250 | 251 | if(!skipTutorial) { 252 | private$currentDeck <- "Tutorial" 253 | private$gameFlow[["Tutorial"]]$`Next Deck` <- gameTypeDecks[1] 254 | } else { 255 | private$currentDeck <- gameTypeDecks[1] 256 | } 257 | }, 258 | 259 | popCard = function() { 260 | if(private$gameFlow[[private$currentDeck]]$`Deck Size` == 0) { 261 | if(private$currentDeck == "Death") return("GAMEOVER") 262 | 263 | private$currentDeck <- private$gameFlow[[private$currentDeck]]$`Next Deck` 264 | } 265 | newSize <- as.numeric(private$gameFlow[[private$currentDeck]]$`Deck Size`) - 1 266 | private$gameFlow[[private$currentDeck]]$`Deck Size` <- newSize 267 | 268 | return(private$generateTemplateCard()) 269 | }, 270 | 271 | initialize = function(dataManager, stateManager) { 272 | self$resetState( 273 | dataManager = dataManager, 274 | stateManager = stateManager 275 | ) 276 | } 277 | ) 278 | ) 279 | -------------------------------------------------------------------------------- /src/app/logic/gameManager.R: -------------------------------------------------------------------------------- 1 | import("R6") 2 | import("utils") 3 | import("jsonlite") 4 | import("glue") 5 | import("shiny") 6 | import("shiny.blank") 7 | import("shiny.grid") 8 | 9 | export("gameManager") 10 | 11 | stateManager <- use("logic/stateManager.R")$stateManager 12 | deckManager <- use("logic/deckManager.R")$deckManager 13 | mapManager <- use("logic/mapManager.R")$mapManager 14 | metricsManager <- use("logic/metricsManager.R")$metricsManager 15 | 16 | dataManager <- use("logic/dataManager.R")$DataManager 17 | 18 | ui_icon <- function(icon, link) { 19 | tags$a( 20 | href = link, 21 | target = "_blank", 22 | onclick = glue::glue("sendAnalyticsEvent({{category: 'social', action: '{icon}', label: '{icon}'}})"), 23 | tags$i(class = glue::glue("nes-icon is-large {icon}")) 24 | ) 25 | } 26 | 27 | game_buttons <- function() { 28 | div( 29 | class = "navigation", 30 | lapply( 31 | list( 32 | list(id = "startGameEasy", text = "Easy Mode"), 33 | list(id = "startGameMedium", text = "Medium Mode"), 34 | list(id = "startGameHard", text = "Hard Mode") 35 | ), 36 | function(options) { 37 | button( 38 | options$id, 39 | options$text, 40 | actions = list( 41 | click = glue::glue(" 42 | modal_gameOverScreen.classList.remove('open') 43 | modal_entryScreen.classList.remove('open'); 44 | modal_attributionScreen.classList.remove('open'); 45 | modal_projectDetailsScreen.classList.remove('open'); 46 | ") 47 | ) 48 | ) 49 | } 50 | ) 51 | ) 52 | } 53 | 54 | # data related to game state 55 | ui <- function() { 56 | tagList( 57 | div( 58 | id = "pause-game", 59 | style = "background-image: url(assets/ui/pause.png)" 60 | ), 61 | tags$script(glue::glue( 62 | '$( document ).ready(function() {{ 63 | $("#pause-game").on("click", function(e){{ 64 | modal_entryScreen.classList.toggle("open"); 65 | modal_attributionScreen.classList.toggle("open"); 66 | modal_projectDetailsScreen.classList.toggle("open"); 67 | }}); 68 | }});' 69 | )), 70 | modal( 71 | "attributionScreen", 72 | content = gridPanel( 73 | class = "entry-screen nes-container is-dark", 74 | 75 | gridPanel( 76 | id = "author-details", 77 | areas = c( 78 | "title title title", 79 | "avatar name name ", 80 | "avatar links links" 81 | ), 82 | rows = "25px 25px 65px", 83 | columns = "115px 110px 110px", 84 | 85 | div(class = "title", "About the author"), 86 | div(class = "name", "Pedro Silva"), 87 | div(class = "avatar", style = "background-image: url(assets/ui/author.png)"), 88 | div( 89 | class = "links", 90 | ui_icon("twitter", "https://twitter.com/sparktuga"), 91 | ui_icon("linkedin", "https://www.linkedin.com/in/pedrocoutinhosilva/"), 92 | ui_icon("github", "https://github.com/pedrocoutinhosilva"), 93 | ) 94 | ) 95 | ), 96 | open = TRUE, 97 | softClose = FALSE, 98 | closeButton = FALSE 99 | ), 100 | modal( 101 | "projectDetailsScreen", 102 | content = gridPanel( 103 | class = "entry-screen nes-container is-dark", 104 | 105 | gridPanel( 106 | id = "project-details", 107 | areas = c( 108 | "title", 109 | "... ", 110 | "links" 111 | ), 112 | rows = "25px 25px 65px", 113 | columns = "165px", 114 | 115 | div(class = "title", "Repository"), 116 | div( 117 | class = "links", 118 | ui_icon("github", "https://github.com/pedrocoutinhosilva/shiny.decisions"), 119 | ) 120 | ) 121 | ), 122 | open = TRUE, 123 | softClose = FALSE, 124 | closeButton = FALSE 125 | ), 126 | modal( 127 | "entryScreen", 128 | content = gridPanel( 129 | class = "entry-screen nes-container is-dark", 130 | areas = c("intro", "options", "navigation"), 131 | rows = "1fr 100px 100px", 132 | 133 | div( 134 | class = "options", 135 | checkbox("showTutorial", "Show tutorial at start", value = TRUE, class = "is-dark") 136 | ), 137 | div( 138 | class = "intro", 139 | p("Welcome to shiny decisions! A game about making the best of bad situations"), 140 | p("Try your best to lead your world in good (and hard) times and see how long you can keep it up!") 141 | ), 142 | game_buttons() 143 | ), 144 | open = TRUE, 145 | softClose = FALSE, 146 | closeButton = FALSE 147 | ), 148 | 149 | modal( 150 | "gameOverScreen", 151 | content = gridPanel( 152 | rows = "1fr 100px", 153 | areas = c("intro", "navigation"), 154 | class = "entry-screen", 155 | 156 | div( 157 | class = "intro", 158 | p(class = "title", "Game over"), 159 | p("You survived for:"), 160 | p(id = "game_over_message", "Week"), 161 | p("Would you like to go again?") 162 | ), 163 | div( 164 | class = "navigation", 165 | button( 166 | "restartGame", 167 | "Back to start", 168 | actions = list( 169 | click = glue::glue(" 170 | modal_gameOverScreen.classList.remove('open'); 171 | modal_entryScreen.classList.add('open'); 172 | modal_attributionScreen.classList.add('open'); 173 | modal_projectDetailsScreen.classList.add('open'); 174 | ") 175 | ) 176 | ) 177 | ) 178 | ), 179 | open = FALSE, 180 | softClose = FALSE, 181 | closeButton = FALSE 182 | ) 183 | ) 184 | } 185 | 186 | gameManager <- R6Class("gameManager", 187 | 188 | private = list( 189 | stateManager = NULL, 190 | deckManager = NULL, 191 | mapManager = NULL, 192 | metricsManager = NULL, 193 | dataManager = NULL, 194 | 195 | session = NULL, 196 | gameType = "Medium", 197 | 198 | resetState = function() { 199 | if (is.null(private$dataManager)) { 200 | private$dataManager <- dataManager$new( 201 | "1LwIPKAxbKvuGyMKktcTVuYZbTda0WMQxmNMhxqaQhGg" 202 | ) 203 | } 204 | private$stateManager <- stateManager$new() 205 | private$metricsManager <- metricsManager$new() 206 | private$mapManager <- mapManager$new(private$stateManager, private$dataManager) 207 | private$deckManager <- deckManager$new(private$dataManager, private$stateManager) 208 | 209 | self$ui = list( 210 | gameStages = ui, 211 | metrics = private$metricsManager$metrics_ui, 212 | karma = private$metricsManager$karma_ui, 213 | map = private$mapManager$ui, 214 | cardStack = private$deckManager$ui 215 | ) 216 | }, 217 | 218 | triggerDeathPhase = function() { 219 | private$deckManager$triggerDeathPhase() 220 | } 221 | ), 222 | 223 | public = list( 224 | ui = NULL, 225 | init_server = function(session) { 226 | private$session <- session 227 | 228 | private$session$sendCustomMessage("init_card_stack", TRUE) 229 | private$metricsManager$init_server("metrics", private$stateManager$state) 230 | private$mapManager$init_server("map") 231 | }, 232 | 233 | resetGame = function(gameType = private$gameType) { 234 | self$startGame(gameType, TRUE) 235 | }, 236 | 237 | startGame = function(gameType, skipTutorial = FALSE) { 238 | private$session$sendCustomMessage( "clear_card_stack", TRUE) 239 | 240 | private$resetState() 241 | private$stateManager$resetState() 242 | private$deckManager$resetState( 243 | gameType, 244 | skipTutorial, 245 | private$dataManager, 246 | private$stateManager 247 | ) 248 | private$mapManager$updateState(private$session) 249 | 250 | private$session$sendCustomMessage( 251 | "trackEvent", 252 | list( 253 | category = "Game state", 254 | action = "Game started", 255 | label = glue::glue("Game started, dificulty {gameType}") 256 | ) 257 | ) 258 | 259 | private$session$sendCustomMessage( "add_card", self$popCard()) 260 | }, 261 | 262 | popCard = function() { 263 | private$deckManager$popCard() 264 | }, 265 | 266 | updateState = function(newState) { 267 | private$stateManager$updateState(newState) 268 | 269 | if(private$stateManager$isDeathState()) { 270 | private$triggerDeathPhase() 271 | } 272 | 273 | private$mapManager$updateState(private$session) 274 | card <- self$popCard() 275 | 276 | if (!is.null(card) && card == "GAMEOVER") { 277 | private$session$sendCustomMessage( 278 | "game_over", 279 | private$stateManager$state$week 280 | ) 281 | private$session$sendCustomMessage( 282 | "trackEvent", 283 | list( 284 | category = "Game state", 285 | action = "Game end", 286 | label = glue::glue("Game ended, score was {private$stateManager$state$week}") 287 | ) 288 | ) 289 | } else { 290 | private$session$sendCustomMessage("add_card", card) 291 | } 292 | }, 293 | 294 | initialize = function() { 295 | private$resetState() 296 | } 297 | ) 298 | ) 299 | -------------------------------------------------------------------------------- /src/app/logic/mapManager.R: -------------------------------------------------------------------------------- 1 | import("R6") 2 | import("utils") 3 | import("jsonlite") 4 | import("glue") 5 | import("shiny.blank") 6 | import("leaflet") 7 | import("shiny") 8 | import("dplyr") 9 | 10 | export("mapManager") 11 | 12 | ui <- function(id) { 13 | ns <- NS(id) 14 | 15 | tagList( 16 | tags$script(src = "scripts/update_map_style.js"), 17 | tags$style(id = "updateMapStyles"), 18 | leafletOutput(ns("mainMap"), height = "100%") 19 | ) 20 | } 21 | 22 | updateMarkers <- function(markers, required, name, map, dataManager) { 23 | # Current markers on the map 24 | if(is.null(markers)) markers = list() 25 | current <- ifelse( 26 | is.data.frame(markers), 27 | nrow(markers), 28 | 0 29 | ) 30 | 31 | # Update marker list with new required side 32 | if(length(markers) == 0) { 33 | markers <- sample_n(dataManager$getCities(), required)[c("lat", "lng")] 34 | } 35 | if(required < current) { 36 | markers <- sample_n(markers, required)[c("lat", "lng")] 37 | } 38 | if(required > current) { 39 | markers <- rbind( 40 | markers, 41 | sample_n(dataManager$getCities(), required - current)[c("lat", "lng")] 42 | ) 43 | } 44 | 45 | # Add new markers to the map 46 | if (length(markers) > 0 ) { 47 | map <- map %>% 48 | addMarkers(data = markers, lng = ~lng, lat = ~lat, 49 | icon = list( 50 | iconUrl = paste0("assets/map/", name, ".png"), 51 | iconSize = c(30, 30), 52 | className = paste0("marker-", name) 53 | )) 54 | } 55 | 56 | return(markers) 57 | } 58 | 59 | server <- function(input, output, session, stateManager, dataManager) { 60 | ns <- session$ns 61 | 62 | output$mainMap <- renderLeaflet({ 63 | leaflet( 64 | options = leafletOptions( 65 | preferCanvas = TRUE, 66 | zoomControl = FALSE, 67 | dragging = FALSE, 68 | doubleClickZoom= FALSE, 69 | minZoom = 2, 70 | maxZoom = 2) 71 | ) %>% 72 | addProviderTiles("Stamen.Watercolor", 73 | options = providerTileOptions(noWrap = TRUE) 74 | ) %>% 75 | setView(0, 0, 2) 76 | }) 77 | 78 | observe({ 79 | map <- leafletProxy("map-mainMap") %>% 80 | clearMarkers() 81 | 82 | current <- reactiveValuesToList(stateManager$state) 83 | markers <- stateManager$markers 84 | 85 | # Base values for calculating necessary number of markers 86 | base <- list( 87 | environment = floor(current$environment/10), 88 | wealth = floor(current$wealth/10), 89 | opinion = floor(current$opinion/10) 90 | ) 91 | 92 | # Marker categories for stats indicators 93 | categories <- list() 94 | # Environment indicators 95 | # Trees start at zero and grow with the current environment 96 | categories$tree <- base$environment * 5 97 | categories$tree_large <- base$environment 98 | categories$tree_small <- base$environment * 2 99 | # Fires appear at 40 environment an increase numbers as it gets lower 100 | categories$fire <- ifelse(current$environment <= 40, (5 - base$environment), 0) 101 | # Cold appear at 40 environment an increase numbers as it gets lower 102 | categories$cold <- ifelse(current$environment <= 40, (5 - base$environment), 0) 103 | # Sick appear at 40 environment an increase numbers as it gets lower 104 | categories$sick <- ifelse(current$environment <= 40, (5 - base$environment), 0) 105 | # Tornados appear at 25 environment an increase numbers as it gets lower 106 | categories$tornado <- ifelse(current$environment <= 25, (3 - base$environment), 0) 107 | # Thunder appear at 25 environment an increase numbers as it gets lower 108 | categories$thunder <- ifelse(current$environment <= 25, (3 - base$environment), 0) 109 | 110 | # Wealth Indicators 111 | # Broken houses start apearing at 50 wealth and increase numbers as it gets lower 112 | categories$house_broken <- ifelse(current$wealth <= 50, (6 - base$wealth), 0) 113 | # Mormal houses grow up to 50 wealth and start decreasing after that 114 | if (current$wealth >= 50) categories$house <- (11 - base$wealth) 115 | else categories$house <- base$wealth 116 | # Office buildings apearing at 50 wealth and increase numbers as it gets higher 117 | categories$office <- ifelse( current$wealth >= 50, (base$wealth - 4), 0) 118 | 119 | # Opinion Indicators 120 | # Mad people start apearing at 50 opinion and increase numbers as it gets lower 121 | categories$mad <- ifelse(current$opinion <= 50, (6 - base$opinion), 0) 122 | # Smily people grow up to 50 opinion and start decreasing after that 123 | if (current$opinion >= 50) categories$smile <- (11 - base$opinion) 124 | else categories$smile <- base$wealth 125 | # Super happy people apearing at 50 opinion and increase numbers as it gets higher 126 | categories$stareyes <- ifelse(current$opinion >= 50, (base$opinion - 4), 0) 127 | 128 | # Updates markers for all categories 129 | for(category in names(categories)) { 130 | stateManager$markers[[category]] <- updateMarkers( 131 | markers = markers[[category]], 132 | required = categories[[category]], 133 | name = category, 134 | map = map, 135 | dataManager = dataManager 136 | ) 137 | } 138 | }) 139 | } 140 | 141 | mapManager <- R6Class("mapManager", 142 | private = list( 143 | server = server, 144 | stateManager = NULL, 145 | dataManager = NULL 146 | ), 147 | 148 | public = list( 149 | ui = ui, 150 | init_server = function(id) { 151 | callModule(private$server, id, private$stateManager, private$dataManager) 152 | }, 153 | 154 | updateState = function(session) { 155 | state <- private$stateManager$state 156 | 157 | session$sendCustomMessage( 158 | "updateMapStyle", 159 | reactiveValuesToList(state) 160 | ) 161 | }, 162 | 163 | initialize = function(stateManager, dataManager) { 164 | private$stateManager <- stateManager 165 | private$dataManager <- dataManager 166 | } 167 | ) 168 | ) 169 | -------------------------------------------------------------------------------- /src/app/logic/metricsManager.R: -------------------------------------------------------------------------------- 1 | import("R6") 2 | import("shiny") 3 | import("glue") 4 | import("shiny.grid") 5 | import("shiny.blank") 6 | 7 | export("metricsManager") 8 | 9 | metricCard <- function(id, label, class, icon) { 10 | div( 11 | class = glue::glue("{class} metric-wrapper ui-element-style"), 12 | 13 | div( 14 | class = "metric-icon", 15 | style = glue::glue("background-image: url('{icon}')") 16 | ), 17 | tags$label(label), 18 | uiOutput(id, class = id) 19 | ) 20 | } 21 | 22 | karma_ui <- function(id) { 23 | ns <- NS(id) 24 | 25 | gridPanel( 26 | class = "metric-karma", 27 | rows = "1fr 50vh 1fr", 28 | columns = "80px", 29 | areas = c( 30 | "...", 31 | "karma-container", 32 | "..." 33 | ), 34 | gridPanel( 35 | class = "karma-container", 36 | rows = "50px 1fr 50px", 37 | columns = "80px", 38 | areas = c( 39 | "karma-good", 40 | "karma-value", 41 | "karma-bad" 42 | ), 43 | 44 | div( 45 | class = "karma-good", 46 | style = "background-image: url('assets/ui/icons/halo.png')" 47 | ), 48 | div( 49 | class = "karma-bad", 50 | style = "background-image: url('assets/ui/icons/evil.png')" 51 | ), 52 | uiOutput(ns("stateKarma"), class = "karma-value") 53 | ) 54 | ) 55 | } 56 | 57 | metrics_ui <- function(id) { 58 | ns <- NS(id) 59 | 60 | gridPanel( 61 | rows = "80px", 62 | columns = "1fr 3fr 1fr 3fr 1fr 3fr 1fr", 63 | areas = c( 64 | "... metric-wealth ... metric-opinion ... metric-environment ..." 65 | ), 66 | class = "metrics", 67 | 68 | metricCard( 69 | ns("stateWealth"), 70 | "Wealth", 71 | "metric-wealth", 72 | "assets/ui/icons/gold.png" 73 | ), 74 | metricCard( 75 | ns("stateOpinion"), 76 | "Opinion", 77 | "metric-opinion", 78 | "assets/map/smile.png" 79 | ), 80 | metricCard( 81 | ns("stateEnvironment"), 82 | "Environment", 83 | "metric-environment", 84 | "assets/map/tree.png" 85 | ) 86 | ) 87 | } 88 | 89 | server <- function(input, output, session, state) { 90 | ns <- session$ns 91 | 92 | output$stateKarma <- renderUI( 93 | div( 94 | class = "karma-wrapper", 95 | progress( 96 | ns("stateKarmaNegative"), 97 | value = ifelse(state$karma < 50, (50 - state$karma) * 2, 0), 98 | type = "is-error negative" 99 | ), 100 | progress( 101 | ns("stateKarmaPositive"), 102 | value = ifelse(state$karma > 49, (state$karma - 50) * 2, 0), 103 | type = "is-primary positive" 104 | ) 105 | ) 106 | ) 107 | output$stateWealth <- renderUI( 108 | progress(ns("stateWealth"), value = state$wealth, type = "is-wealth") 109 | ) 110 | output$stateOpinion <- renderUI( 111 | progress(ns("stateOpinion"), value = state$opinion, type = "is-opinion") 112 | ) 113 | output$stateEnvironment <- renderUI( 114 | progress(ns("stateEnvironment"), value = state$environment, type = "is-environment") 115 | ) 116 | } 117 | 118 | # Manages the UI displaying the state metrics. 119 | metricsManager <- R6Class("metricsManager", 120 | private = list( 121 | server = server 122 | ), 123 | 124 | public = list( 125 | karma_ui = karma_ui, 126 | metrics_ui = metrics_ui, 127 | init_server = function(id, state) { 128 | callModule(private$server, id, state) 129 | } 130 | ) 131 | ) 132 | -------------------------------------------------------------------------------- /src/app/logic/stateManager.R: -------------------------------------------------------------------------------- 1 | import("R6") 2 | import("shiny") 3 | 4 | export("stateManager") 5 | 6 | # Manages information about the current state of the game 7 | stateManager <- R6Class("stateManager", 8 | private = list( 9 | default_state = list( 10 | karma = 60, 11 | wealth = 50, 12 | opinion = 50, 13 | environment = 50, 14 | week = 1 15 | ) 16 | ), 17 | public = list( 18 | # Metrics for game state 19 | state = reactiveValues( 20 | karma = 50, 21 | wealth = 0, 22 | opinion = 0, 23 | environment = 0, 24 | week = 0 25 | ), 26 | # Markers currently in the map 27 | markers = list(), 28 | 29 | # Allows reseting the state of the manager. 30 | resetState = function(state = private$default_state) { 31 | isolate(self$updateState(state, TRUE)) 32 | self$markers = list() 33 | }, 34 | 35 | # Checks for death states 36 | isDeathState = function() { 37 | if(self$state$wealth < 1 || 38 | self$state$opinion < 1 || 39 | self$state$environment < 1 40 | ) { 41 | return(TRUE) 42 | } 43 | 44 | return(FALSE) 45 | }, 46 | 47 | # If force is false, newState contains the delta values to update 48 | # If force is true, newState contains the new values for the state attribute 49 | updateState = function(newState, force = FALSE) { 50 | lapply(names(newState), function(attribute) { 51 | self$state[[attribute]] <- ifelse ( 52 | force, 53 | newState[[attribute]], 54 | self$state[[attribute]] + as.numeric(newState[[attribute]]) 55 | ) 56 | 57 | # Ignore week limits 58 | if(attribute != "week") { 59 | if(self$state[[attribute]] < 0) 60 | self$state[[attribute]] <- 0 61 | if(self$state[[attribute]] > 100) 62 | self$state[[attribute]] <- 100 63 | } 64 | }) 65 | } 66 | ) 67 | ) 68 | -------------------------------------------------------------------------------- /src/app/server.R: -------------------------------------------------------------------------------- 1 | function(input, output, session) { 2 | 3 | session$userData$gameManager <- use("logic/gameManager.R")$gameManager$new() 4 | session$userData$gameManager$init_server(session) 5 | 6 | observeEvent(input$update_state, { 7 | session$userData$gameManager$updateState(input$update_state) 8 | }) 9 | 10 | observeEvent(input$startGameEasy, { 11 | session$userData$gameManager$startGame("Easy", !input$showTutorial) 12 | }) 13 | observeEvent(input$startGameMedium, { 14 | session$userData$gameManager$startGame("Medium", !input$showTutorial) 15 | }) 16 | observeEvent(input$startGameHard, { 17 | session$userData$gameManager$startGame("Hard", !input$showTutorial) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /src/app/styles/main.scss: -------------------------------------------------------------------------------- 1 | //Modules and variables 2 | @import "modules/config"; 3 | @import "modules/animations"; 4 | @import "modules/static"; 5 | 6 | //Modules and variables 7 | @import "partials/layout"; 8 | @import "partials/modals"; 9 | @import "partials/metrics"; 10 | @import "partials/cards"; 11 | @import "partials/map"; 12 | -------------------------------------------------------------------------------- /src/app/styles/modules/_animations.scss: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes color-change { 2 | 0% { color: white; } 3 | 50% { color: black; } 4 | 100% { color: white; } 5 | } 6 | @-moz-keyframes color-change { 7 | 0% { color: white; } 8 | 50% { color: black; } 9 | 100% { color: white; } 10 | } 11 | @-ms-keyframes color-change { 12 | 0% { color: white; } 13 | 50% { color: black; } 14 | 100% { color: white; } 15 | } 16 | @-o-keyframes color-change { 17 | 0% { color: white; } 18 | 50% { color: black; } 19 | 100% { color: white; } 20 | } 21 | @keyframes color-change { 22 | 0% { color: white; } 23 | 50% { color: black; } 24 | 100% { color: white; } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/styles/modules/_config.scss: -------------------------------------------------------------------------------- 1 | $main-background-color: #212529; 2 | 3 | $progress-background-color: #fff; 4 | $progress-opinion-fill: #6853A3; 5 | $progress-wealth-fill: #e48e22; 6 | $progress-environment-fill: #6cab22; 7 | -------------------------------------------------------------------------------- /src/app/styles/modules/_static.scss: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | width: 100%; 4 | overflow: hidden; 5 | } 6 | 7 | .modal-content, 8 | .ui-element-style { 9 | filter: drop-shadow(5px 10px 15px black); 10 | } 11 | -------------------------------------------------------------------------------- /src/app/styles/partials/_cards.scss: -------------------------------------------------------------------------------- 1 | .app-cards { 2 | overflow: visible; 3 | z-index: 1; 4 | } 5 | 6 | #card_stack_wrapper { 7 | width: 100%; 8 | height: 100%; 9 | position: relative; 10 | overflow: visible; 11 | } 12 | 13 | #card_stack { 14 | width: 100%; 15 | height: 100%; 16 | position: absolute; 17 | bottom: 0; 18 | overflow: hidden; 19 | overflow: visible; 20 | 21 | .card { 22 | border: 3px solid #212529; 23 | position: absolute; 24 | top: 50%; 25 | left: 50%; 26 | border-radius: 1%; 27 | filter: drop-shadow(25px 25px 25px black); 28 | background-color: white; 29 | transform: translateX(-50%) translateY(-50%) scale(0.95); 30 | background-position: center center; 31 | background-repeat: no-repeat; 32 | width: 30vh; 33 | height: 30vh; 34 | max-width: 70vw; 35 | max-height: 70vw; 36 | font-size: 1rem; 37 | 38 | .card-background { 39 | position: absolute; 40 | top: 0; 41 | bottom: 0; 42 | left: 0; 43 | right: 0; 44 | } 45 | 46 | .card-image { 47 | z-index: 2; 48 | background-size: 90% 90% !important; 49 | background-repeat: no-repeat !important; 50 | background-position: 50% 220% !important; 51 | filter: drop-shadow(7px 1px 0px #3b3c3e); 52 | } 53 | .card-color { 54 | z-index: 1; 55 | filter: blur(10px); 56 | } 57 | } 58 | 59 | .message-left, .message-right { 60 | display: none; 61 | padding: 5px; 62 | position: absolute; 63 | z-index: 4; 64 | left: 8px; 65 | right: 8px; 66 | top: 8px; 67 | color: black; 68 | } 69 | 70 | .message-left { 71 | text-align: right; 72 | right: 8px; 73 | } 74 | 75 | .dragging-left .message-left, 76 | .dragging-right .message-right { 77 | display: block; 78 | } 79 | } 80 | 81 | #card_stack_message{ 82 | width: 100%; 83 | height: 150px; 84 | display: flex; 85 | align-items: center; 86 | justify-content: center; 87 | text-align: center; 88 | background-color: $main-background-color; 89 | color: white; 90 | padding: 30px; 91 | position: relative; 92 | 93 | &:before, &:after { 94 | content: ""; 95 | position: absolute; 96 | top: 0; 97 | z-index: -1; 98 | border-top: 150px solid transparent; 99 | } 100 | 101 | &:before { 102 | left: -40px; 103 | border-right: 40px solid $main-background-color; 104 | } 105 | 106 | &:after { 107 | right: -40px; 108 | border-left: 40px solid $main-background-color; 109 | } 110 | } 111 | 112 | .week-content { 113 | text-align: center; 114 | font-size: 28px; 115 | filter: drop-shadow(5px 10px 15px black); 116 | -webkit-text-stroke-width: 2px; 117 | -webkit-text-stroke-color: #ffffff33; 118 | } 119 | 120 | @media (orientation: portrait) { 121 | .card { 122 | font-size: 140% !important; 123 | } 124 | 125 | #card_stack_message { 126 | font-size: 140% !important; 127 | height: 90% !important; 128 | background-color: transparent; 129 | } 130 | #card_stack_message:after, 131 | #card_stack_message:before { 132 | display: none !important; 133 | } 134 | 135 | .week-content { 136 | color: white; 137 | line-height: 80px; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/app/styles/partials/_layout.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: $main-background-color !important; 3 | } 4 | 5 | @media (orientation: portrait) { 6 | #page-wrapper { 7 | grid-template-areas: 8 | 'app-metrics app-metrics app-metrics' 9 | '... app-week ...' 10 | 'app-karma app-task ...' 11 | 'app-karma app-task ...' 12 | 'app-karma app-cards ...' 13 | 'app-karma ... ...' !important; 14 | grid-template-rows: 80px 80px 20% 50px 1fr 20% !important; 15 | grid-template-columns: 80px 1fr 80px !important; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/styles/partials/_map.scss: -------------------------------------------------------------------------------- 1 | .app-map { 2 | position: absolute; 3 | height: 100%; 4 | width: 100%; 5 | z-index: -1; 6 | } 7 | 8 | .leaflet-marker-icon { 9 | filter: drop-shadow(2px 2px 0 black) drop-shadow(-2px -2px 0 black); 10 | } 11 | 12 | .marker-tree, 13 | .marker-tree_large, 14 | .marker-tree_small { 15 | z-index: 1 !important; 16 | } 17 | 18 | #map-mainMap { 19 | background: url("//stamen-tiles-a.a.ssl.fastly.net/watercolor/2/4/0.png"); 20 | background-size: contain; 21 | } 22 | 23 | @media (orientation: portrait) { 24 | .app-map { 25 | height: 40vh !important; 26 | bottom: 10%; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/styles/partials/_metrics.scss: -------------------------------------------------------------------------------- 1 | .app-metrics { 2 | color: #fff; 3 | z-index: 3; 4 | } 5 | 6 | .metric-wrapper { 7 | padding-left: 55px; 8 | position: relative; 9 | background-color: $main-background-color; 10 | 11 | &:before, &:after { 12 | content: ""; 13 | position: absolute; 14 | z-index: -1; 15 | top: 0; 16 | border-bottom: 80px solid transparent; 17 | } 18 | &:before { 19 | left: -20px; 20 | border-right: 20px solid $main-background-color; 21 | } 22 | &:after { 23 | right: -20px; 24 | border-left: 20px solid $main-background-color; 25 | } 26 | 27 | &.will-change, 28 | &.will-change-large { 29 | -webkit-animation: color-change 1s infinite; 30 | -moz-animation: color-change 1s infinite; 31 | -o-animation: color-change 1s infinite; 32 | -ms-animation: color-change 1s infinite; 33 | animation: color-change 1s infinite; 34 | } 35 | 36 | label { 37 | margin-top: 12px; 38 | margin-bottom: 0; 39 | } 40 | 41 | progress { 42 | width: calc(100% - 20px); 43 | } 44 | 45 | .metric-icon { 46 | position: absolute; 47 | left: 7px; 48 | top: 0; 49 | bottom: 0; 50 | margin: auto; 51 | width: 40px; 52 | height: 40px; 53 | background-position: center center; 54 | background-size: 80%; 55 | background-repeat: no-repeat; 56 | } 57 | } 58 | 59 | .metric-karma { 60 | z-index: 100; 61 | 62 | .karma-container { 63 | background-color: $main-background-color; 64 | 65 | .karma-bad, 66 | .karma-good { 67 | background-position: center center; 68 | background-repeat: no-repeat; 69 | position: relative; 70 | } 71 | 72 | .karma-bad { 73 | background-size: 34px; 74 | background-position: 20px; 75 | } 76 | 77 | .karma-good:before { 78 | content: ""; 79 | position: absolute; 80 | top: -20px; 81 | z-index: -1; 82 | border-left: 80px solid #212529; 83 | border-top: 20px solid transparent; 84 | } 85 | .karma-bad:after { 86 | content: ""; 87 | position: absolute; 88 | bottom: -20px; 89 | z-index: -1; 90 | border-left: 80px solid #212529; 91 | border-bottom: 20px solid transparent; 92 | } 93 | } 94 | 95 | .karma-value { 96 | width: calc(50vh - 100px); 97 | transform: translate(29px, 0) rotate(-90deg); 98 | 99 | .karma-wrapper { 100 | display: flex; 101 | 102 | .positive{ 103 | width: 50%; 104 | float: right; 105 | padding-left: 4px; 106 | } 107 | 108 | .negative{ 109 | transform: rotate(180deg); 110 | width: 50%; 111 | float: left; 112 | } 113 | } 114 | } 115 | } 116 | 117 | .nes-progress { 118 | height: 20px !important; 119 | margin: 0 !important; 120 | 121 | &.is-opinion { 122 | &::-webkit-progress-bar { 123 | background-color: $progress-background-color; 124 | } 125 | &::-webkit-progress-value { 126 | background-color: $progress-opinion-fill; 127 | } 128 | &::-moz-progress-bar { 129 | background-color: $progress-opinion-fill; 130 | } 131 | &::-ms-fill { 132 | background-color: $progress-opinion-fill; 133 | border: none 134 | } 135 | } 136 | 137 | &.is-wealth { 138 | &::-webkit-progress-bar { 139 | background-color: $progress-background-color; 140 | } 141 | &::-webkit-progress-value { 142 | background-color: $progress-wealth-fill; 143 | } 144 | &::-moz-progress-bar { 145 | background-color: $progress-wealth-fill; 146 | } 147 | &::-ms-fill { 148 | background-color: $progress-wealth-fill; 149 | border: none 150 | } 151 | } 152 | 153 | &.is-environment { 154 | &::-webkit-progress-bar { 155 | background-color: $progress-background-color; 156 | } 157 | &::-webkit-progress-value { 158 | background-color: $progress-environment-fill; 159 | } 160 | &::-moz-progress-bar { 161 | background-color: $progress-environment-fill; 162 | } 163 | &::-ms-fill { 164 | background-color: $progress-environment-fill; 165 | border: none 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/app/styles/partials/_modals.scss: -------------------------------------------------------------------------------- 1 | .modal { 2 | background-color: rgba(0, 0, 0, 0.8) !important; 3 | } 4 | 5 | 6 | #gameOverScreen { 7 | z-index: 100100 !important; 8 | 9 | .title, #game_over_message { 10 | font-size: 300%; 11 | } 12 | } 13 | 14 | .entry-screen { 15 | text-align: center; 16 | justify-content: center; 17 | align-items: center; 18 | 19 | button { 20 | margin: 15px; 21 | } 22 | } 23 | 24 | @media (orientation: portrait) { 25 | .entry-screen { 26 | grid-template-rows: 1fr 150px !important; 27 | font-size: 140% !important; 28 | } 29 | } 30 | 31 | #entryScreen { 32 | .modal-content { 33 | margin-top: 10vh !important; 34 | margin-bottom: calc(15vh + 200px) !important; 35 | } 36 | } 37 | 38 | #attributionScreen { 39 | pointer-events: none; 40 | 41 | background: transparent !important; 42 | z-index: 10001 !important; 43 | .modal-content { 44 | .title { 45 | text-decoration: underline; 46 | } 47 | 48 | .links { 49 | pointer-events: all; 50 | } 51 | 52 | background: transparent !important; 53 | 54 | width: 427px !important; 55 | height: 200px !important; 56 | padding: 0 !important; 57 | 58 | margin-left: 10vw !important; 59 | margin-bottom: 10vh !important; 60 | 61 | @media (orientation: portrait) { 62 | font-size: 75%; 63 | } 64 | } 65 | 66 | #author-details { 67 | text-align: left; 68 | grid-gap: 10px; 69 | margin: 0 !important; 70 | 71 | .avatar { 72 | background: url("/assets/ui/author.png"); 73 | background-size: contain; 74 | background-repeat: no-repeat; 75 | } 76 | } 77 | } 78 | 79 | #projectDetailsScreen { 80 | pointer-events: none; 81 | 82 | background: transparent !important; 83 | z-index: 10001 !important; 84 | .modal-content { 85 | 86 | .title { 87 | text-decoration: underline; 88 | } 89 | 90 | .links { 91 | pointer-events: all; 92 | } 93 | 94 | background: transparent !important; 95 | 96 | width: 237px !important; 97 | height: 200px !important; 98 | padding: 0 !important; 99 | 100 | margin-right: 10vw !important; 101 | margin-bottom: 10vh !important; 102 | 103 | @media (orientation: portrait) { 104 | font-size: 75%; 105 | } 106 | } 107 | 108 | #project-details { 109 | text-align: left; 110 | grid-gap: 10px; 111 | margin: 0 !important; 112 | 113 | .avatar { 114 | background-size: contain; 115 | background-repeat: no-repeat; 116 | } 117 | } 118 | } 119 | 120 | #pause-game { 121 | position: absolute; 122 | bottom: 10px; 123 | left: 10px; 124 | height: 1.5cm; 125 | width: 1.5cm; 126 | z-index: 10010; 127 | background-size: contain; 128 | cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAzElEQVRYR+2X0Q6AIAhF5f8/2jYXZkwEjNSVvVUjDpcrGgT7FUkI2D9xRfQETwNIiWO85wfINfQUEyxBG2ArsLwC0jioGt5zFcwF4OYDPi/mBYKm4t0U8ATgRm3ThFoAqkhNgWkA0jJLvaOVSs7j3qMnSgXWBMiWPXe94QqMBMBc1VZIvaTu5u5pQewq0EqNZvIEMCmxAawK0DNkay9QmfFNAJUXfgGgUkLaE7j/h8fnASkxHTz0DGIBMCnBeeM7AArpUd3mz2x3C7wADglA8BcWMZhZAAAAAElFTkSuQmCC) 14 0, pointer; 129 | background-color: #d1d1d1; 130 | border-radius: 50%; 131 | image-rendering: pixelated; 132 | filter: hue-rotate(-160deg) drop-shadow(2px 4px 6px black); 133 | } 134 | -------------------------------------------------------------------------------- /src/app/ui.R: -------------------------------------------------------------------------------- 1 | blankPage( 2 | title = "Shiny Decisions", 3 | theme = "nes", 4 | 5 | tags$head( 6 | tags$head(includeHTML(("google-analytics.html"))), 7 | tags$script(src = "scripts/analytics-events.js"), 8 | tags$link(rel="shortcut icon", href="assets/map/tree.png"), 9 | tags$meta(name="apple-mobile-web-app-capable", content="yes"), 10 | tags$link(rel = "stylesheet", type = "text/css", href = "styles/sass.min.css") 11 | ), 12 | 13 | gameManager$ui$gameStages(), 14 | 15 | div( 16 | class = "app-map", 17 | gameManager$ui$map("map") 18 | ), 19 | gridPanel( 20 | id = "page-wrapper", 21 | rows = "100px 1fr 50px 150px", 22 | columns = "80px 1fr 1fr 1fr 1fr 1fr 1fr 80px", 23 | areas = c( 24 | "app-metrics app-metrics app-metrics app-metrics app-metrics app-metrics app-metrics app-metrics", 25 | "app-karma ... ... ... app-cards app-cards app-cards app-cards", 26 | "app-karma ... ... app-week app-week ... ... ...", 27 | "... ... app-task app-task app-task app-task ... ..." 28 | ), 29 | 30 | gridPanel( 31 | class = "app-metrics", 32 | gameManager$ui$metrics("metrics") 33 | ), 34 | 35 | gridPanel( 36 | class = "app-karma ui-element-style", 37 | gameManager$ui$karma("metrics") 38 | ), 39 | 40 | gridPanel( 41 | class = "app-cards", 42 | gameManager$ui$cardStack() 43 | ), 44 | 45 | div( 46 | id = "app_week", 47 | class = "app-week", 48 | p(class = "week-content") 49 | ), 50 | 51 | div( 52 | id = "card_stack_message", 53 | class = "app-task ui-element-style", 54 | p(class = "message-content") 55 | ) 56 | ) 57 | ) 58 | -------------------------------------------------------------------------------- /src/app/www/assets/cards/beekeeper-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/beekeeper-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/blacksmith-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/blacksmith-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/blank.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/boy-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/boy-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/boy-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/boy-2.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/boy-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/boy-3.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/boy-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/boy-4.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/boy-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/boy-5.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/boy-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/boy-6.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/boy-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/boy-7.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/chef-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/chef-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/doctor-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/doctor-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/farmer-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/farmer-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/girl-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/girl-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/girl-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/girl-2.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/girl-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/girl-3.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/girl-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/girl-4.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/hunter-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/hunter-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-10.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-11.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-12.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-13.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-14.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-15.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-16.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-17.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-18.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-19.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-2.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-20.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-21.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-22.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-23.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-24.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-25.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-26.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-27.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-28.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-29.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-3.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-30.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-31.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-32.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-33.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-34.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-35.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-36.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-37.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-4.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-5.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-6.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-7.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-8.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/man-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/man-9.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/nun-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/nun-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/pirate-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/pirate-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/pirate-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/pirate-2.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/sailor-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/sailor-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/santa-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/santa-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/scientist-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/scientist-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/shady-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/shady-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/shady-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/shady-2.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/shady-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/shady-3.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-1.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-10.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-11.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-12.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-13.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-14.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-16.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-17.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-18.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-19.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-2.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-20.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-21.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-22.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-23.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-24.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-25.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-26.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-6.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-7.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-8.png -------------------------------------------------------------------------------- /src/app/www/assets/cards/woman-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/cards/woman-9.png -------------------------------------------------------------------------------- /src/app/www/assets/map/cold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/cold.png -------------------------------------------------------------------------------- /src/app/www/assets/map/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/fire.png -------------------------------------------------------------------------------- /src/app/www/assets/map/house.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/house.png -------------------------------------------------------------------------------- /src/app/www/assets/map/house_broken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/house_broken.png -------------------------------------------------------------------------------- /src/app/www/assets/map/mad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/mad.png -------------------------------------------------------------------------------- /src/app/www/assets/map/mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/mask.png -------------------------------------------------------------------------------- /src/app/www/assets/map/office.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/office.png -------------------------------------------------------------------------------- /src/app/www/assets/map/sick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/sick.png -------------------------------------------------------------------------------- /src/app/www/assets/map/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/smile.png -------------------------------------------------------------------------------- /src/app/www/assets/map/stareyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/stareyes.png -------------------------------------------------------------------------------- /src/app/www/assets/map/thunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/thunder.png -------------------------------------------------------------------------------- /src/app/www/assets/map/tornado.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/tornado.png -------------------------------------------------------------------------------- /src/app/www/assets/map/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/tree.png -------------------------------------------------------------------------------- /src/app/www/assets/map/tree_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/tree_large.png -------------------------------------------------------------------------------- /src/app/www/assets/map/tree_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/map/tree_small.png -------------------------------------------------------------------------------- /src/app/www/assets/ui/author.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/ui/author.png -------------------------------------------------------------------------------- /src/app/www/assets/ui/icons/evil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/ui/icons/evil.png -------------------------------------------------------------------------------- /src/app/www/assets/ui/icons/friend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/ui/icons/friend.png -------------------------------------------------------------------------------- /src/app/www/assets/ui/icons/gold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/ui/icons/gold.png -------------------------------------------------------------------------------- /src/app/www/assets/ui/icons/halo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/ui/icons/halo.png -------------------------------------------------------------------------------- /src/app/www/assets/ui/icons/salad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/ui/icons/salad.png -------------------------------------------------------------------------------- /src/app/www/assets/ui/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrocoutinhosilva/shiny.decisions/4db92763bdc885b18d34cdb0bd53b301c4978964/src/app/www/assets/ui/pause.png -------------------------------------------------------------------------------- /src/app/www/scripts/analytics-events.js: -------------------------------------------------------------------------------- 1 | let sendAnalyticsEvent = function(options) { 2 | gtag('event', options.action, {'event_category' : options.category, 3 | 'event_label' : options.label}); 4 | } 5 | Shiny.addCustomMessageHandler('trackEvent', sendAnalyticsEvent) 6 | -------------------------------------------------------------------------------- /src/app/www/scripts/card_stack.js: -------------------------------------------------------------------------------- 1 | class Carousel { 2 | constructor(element) { 3 | this.board = element 4 | this.handle() 5 | } 6 | 7 | handle() { 8 | this.cards = this.board.querySelectorAll('.card') 9 | this.topCard = this.cards[this.cards.length-1] 10 | this.nextCard = this.cards[this.cards.length-2] 11 | if (this.cards.length > 0) { 12 | 13 | this.topCard.style.transform = 14 | 'translateX(-50%) translateY(-50%) rotate(0deg) rotateY(0deg) scale(1)' 15 | 16 | if (this.hammer) this.hammer.destroy() 17 | 18 | this.hammer = new Hammer(this.topCard) 19 | this.hammer.add(new Hammer.Tap()) 20 | this.hammer.add(new Hammer.Pan({ 21 | position: Hammer.position_ALL, threshold: 0 22 | })) 23 | 24 | this.hammer.on('tap', (e) => { this.onTap(e) }) 25 | this.hammer.on('pan', (e) => { this.onPan(e) }) 26 | 27 | document.querySelector('#card_stack_message p').textContent = this.topCard.getAttribute('message-task') 28 | document.querySelector('#app_week p').textContent = this.topCard.getAttribute('week-text') 29 | 30 | } 31 | 32 | } 33 | 34 | onTap(e) { 35 | 36 | let propX = (e.center.x - e.target.getBoundingClientRect().left) / e.target.clientWidth 37 | 38 | let rotateY = 15 * (propX < 0.05 ? -1 : 1) 39 | 40 | this.topCard.style.transition = 'transform 100ms ease-out' 41 | 42 | this.topCard.style.transform = 43 | 'translateX(-50%) translateY(-50%) rotate(0deg) rotateY(' + rotateY + 'deg) scale(1)' 44 | 45 | setTimeout(() => { 46 | this.topCard.style.transform = 47 | 'translateX(-50%) translateY(-50%) rotate(0deg) rotateY(0deg) scale(1)' 48 | }, 100) 49 | 50 | } 51 | 52 | onPan(e) { 53 | 54 | if (!this.isPanning) { 55 | 56 | this.isPanning = true 57 | 58 | this.topCard.style.transition = null 59 | if (this.nextCard) this.nextCard.style.transition = null 60 | 61 | let style = window.getComputedStyle(this.topCard) 62 | let mx = style.transform.match(/^matrix\\((.+)\\)$/) 63 | this.startPosX = mx ? parseFloat(mx[1].split(', ')[4]) : 0 64 | this.startPosY = mx ? parseFloat(mx[1].split(', ')[5]) : 0 65 | 66 | let bounds = this.topCard.getBoundingClientRect() 67 | 68 | this.isDraggingFrom = 69 | (e.center.y - bounds.top) > this.topCard.clientHeight / 2 ? -1 : 1 70 | 71 | } 72 | 73 | let posX = e.deltaX + this.startPosX 74 | let posY = e.deltaY + this.startPosY 75 | 76 | let propX = e.deltaX / this.board.clientWidth 77 | let propY = e.deltaY / this.board.clientHeight 78 | 79 | let dirX = e.deltaX < 0 ? -1 : 1 80 | 81 | let delta_threshold = 10 82 | let delta_direction = '' 83 | 84 | if(dirX > 0) { 85 | this.topCard.classList.add('dragging-right') 86 | this.topCard.classList.remove('dragging-left') 87 | delta_direction = 'right' 88 | } else { 89 | this.topCard.classList.add('dragging-left') 90 | this.topCard.classList.remove('dragging-right') 91 | delta_direction = 'left' 92 | } 93 | 94 | Object.values(document.querySelectorAll(`.metric-wrapper`)).map(x => { 95 | x.classList.remove('will-change', 'will-change-large') 96 | }) 97 | 98 | Object.entries({ 99 | karma: Number(carousel.topCard.getAttribute(`delta-${delta_direction}-karma`)), 100 | wealth: Number(carousel.topCard.getAttribute(`delta-${delta_direction}-wealth`)), 101 | opinion: Number(carousel.topCard.getAttribute(`delta-${delta_direction}-opinion`)), 102 | environment: Number(carousel.topCard.getAttribute(`delta-${delta_direction}-environment`)) 103 | }).map(attribute => { 104 | if(attribute[1] !== 0) { 105 | document.querySelector(`.metric-${attribute[0]}`) 106 | .classList.add( 107 | (attribute[1] > delta_threshold) 108 | ? 'will-change-large' 109 | : 'will-change' 110 | ) 111 | } 112 | }) 113 | 114 | let deg = this.isDraggingFrom * dirX * Math.abs(propX) * 45 115 | 116 | let scale = (95 + (5 * Math.abs(propX))) / 100 117 | 118 | this.topCard.style.transform = 119 | 'translateX(calc(' + posX + 'px - 50%)) translateY(calc(' + posY + 'px - 50%)) rotate(' + deg + 'deg) rotateY(0deg) scale(1)' 120 | 121 | 122 | if (this.nextCard) this.nextCard.style.transform = 123 | 'translateX(-50%) translateY(-50%) rotate(0deg) rotateY(0deg) scale(' + scale + ')' 124 | 125 | if (e.isFinal) { 126 | this.isPanning = false 127 | let successful = false 128 | 129 | let direction = '' 130 | 131 | this.topCard.classList.remove('dragging-left') 132 | this.topCard.classList.remove('dragging-right') 133 | 134 | Object.values(document.querySelectorAll(`.metric-wrapper`)).map(x => { 135 | x.classList.remove('will-change', 'will-change-large') 136 | }) 137 | 138 | this.topCard.style.transition = 'transform 200ms ease-out' 139 | if (this.nextCard) this.nextCard.style.transition = 'transform 100ms linear' 140 | 141 | if (propX > 0.15 && e.direction == Hammer.DIRECTION_RIGHT) { 142 | 143 | direction = 'RIGHT' 144 | successful = true 145 | posX = this.board.clientWidth 146 | 147 | } else if (propX < -0.15 && e.direction == Hammer.DIRECTION_LEFT) { 148 | 149 | direction = 'LEFT' 150 | successful = true 151 | posX = - (this.board.clientWidth + this.topCard.clientWidth) 152 | 153 | } 154 | 155 | if (successful) { 156 | 157 | this.topCard.style.transform = 158 | 'translateX(calc(' + posX + 'px - 50%)) translateY(calc(' + posY + 'px - 50%)) rotate(' + deg + 'deg)' 159 | 160 | let delta = {} 161 | 162 | if(direction == 'LEFT') { 163 | delta = { 164 | karma: this.topCard.getAttribute('delta-left-karma'), 165 | wealth: this.topCard.getAttribute('delta-left-wealth'), 166 | opinion: this.topCard.getAttribute('delta-left-opinion'), 167 | environment: this.topCard.getAttribute('delta-left-environment') 168 | } 169 | } 170 | 171 | if(direction == 'RIGHT') { 172 | delta = { 173 | karma: this.topCard.getAttribute('delta-right-karma'), 174 | wealth: this.topCard.getAttribute('delta-right-wealth'), 175 | opinion: this.topCard.getAttribute('delta-right-opinion'), 176 | environment: this.topCard.getAttribute('delta-right-environment') 177 | } 178 | } 179 | 180 | setTimeout(() => { 181 | Shiny.setInputValue('update_state', { 182 | karma: delta.karma, 183 | wealth: delta.wealth, 184 | opinion: delta.opinion, 185 | environment: delta.environment, 186 | week: this.topCard.getAttribute('week-increment') 187 | }, {priority : 'event'}) 188 | 189 | this.board.removeChild(this.topCard) 190 | this.handle() 191 | 192 | }, 200) 193 | 194 | } else { 195 | 196 | this.topCard.style.transform = 197 | 'translateX(-50%) translateY(-50%) rotate(0deg) rotateY(0deg) scale(1)' 198 | if (this.nextCard) this.nextCard.style.transform = 199 | 'translateX(-50%) translateY(-50%) rotate(0deg) rotateY(0deg) scale(0.95)' 200 | 201 | } 202 | 203 | } 204 | 205 | } 206 | 207 | push({ background, message, delta }) { 208 | let card = document.createElement('div') 209 | 210 | let card_color = document.createElement('div') 211 | let card_image = document.createElement('div') 212 | 213 | let message_left = document.createElement('p') 214 | let message_right = document.createElement('p') 215 | 216 | message_left.classList.add('message-left') 217 | message_left.textContent = message.left 218 | 219 | message_right.classList.add('message-right') 220 | message_right.textContent = message.right 221 | 222 | card.append(message_left) 223 | card.append(message_right) 224 | 225 | card.append(card_image) 226 | card.append(card_color) 227 | 228 | card.classList.add('card') 229 | 230 | Object.entries({ 231 | 'message-task': message.task, 232 | 'message-left': message.left, 233 | 'message-right': message.right, 234 | 235 | 'week-text': message.week.text, 236 | 'week-increment': message.week.increment, 237 | 238 | 'delta-left-karma': delta.left.karma, 239 | 'delta-left-wealth': delta.left.wealth, 240 | 'delta-left-opinion': delta.left.opinion, 241 | 'delta-left-environment': delta.left.environment, 242 | 243 | 'delta-right-karma': delta.right.karma, 244 | 'delta-right-wealth': delta.right.wealth, 245 | 'delta-right-opinion': delta.right.opinion, 246 | 'delta-right-environment': delta.right.environment 247 | }).map(attribute => { 248 | card.setAttribute(attribute[0], attribute[1]) 249 | }) 250 | 251 | card_image.classList.add('card-background', 'card-image') 252 | card_color.classList.add('card-background', 'card-color') 253 | 254 | card_color.style.background = `linear-gradient( 255 | 135deg, 256 | ${background.color_left} 0%, 257 | ${background.color_left} 35%, 258 | ${background.color_right} 65%, 259 | ${background.color_right} 100%)` 260 | 261 | card_image.style.background = 262 | `url(${background.image})` 263 | 264 | if (this.board.firstChild) { 265 | this.board.insertBefore(card, this.board.firstChild) 266 | } else { 267 | this.board.append(card) 268 | } 269 | 270 | } 271 | } 272 | 273 | let carousel 274 | 275 | let init_card_stack = function(force) { 276 | carousel = new Carousel(document.querySelector('#card_stack')) 277 | } 278 | Shiny.addCustomMessageHandler('init_card_stack', init_card_stack); 279 | 280 | let addCard = function(options) { 281 | carousel.push(options) 282 | carousel.handle() 283 | } 284 | Shiny.addCustomMessageHandler('add_card', addCard) 285 | 286 | let gameOver = function(message) { 287 | modal_gameOverScreen.classList.add('open') 288 | document.getElementById('game_over_message').innerHTML = message 289 | } 290 | Shiny.addCustomMessageHandler('game_over', gameOver) 291 | 292 | let clearCardStack = function(message) { 293 | $("#card_stack .card").remove() 294 | } 295 | Shiny.addCustomMessageHandler('clear_card_stack', clearCardStack) 296 | -------------------------------------------------------------------------------- /src/app/www/scripts/filter-toggler.js: -------------------------------------------------------------------------------- 1 | $( document ).ready(function() { 2 | $(document).on('click','#toggle-network-filters',function(){ 3 | $("#network-filters-cell").toggleClass("open"); 4 | 5 | let button_text = $("#network-filters-cell").hasClass("open") 6 | ? "Close Filters" 7 | : "Open Filters" 8 | 9 | $("#toggle-network-filters").html(button_text); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /src/app/www/scripts/hammer.min.js: -------------------------------------------------------------------------------- 1 | /*! Hammer.JS - v2.0.7 - 2016-04-22 2 | * http://hammerjs.github.io/ 3 | * 4 | * Copyright (c) 2016 Jorik Tangelder; 5 | * Licensed under the MIT license */ 6 | !function(a,b,c,d){"use strict";function e(a,b,c){return setTimeout(j(a,c),b)}function f(a,b,c){return Array.isArray(a)?(g(a,c[b],c),!0):!1}function g(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",f=a.console&&(a.console.warn||a.console.log);return f&&f.call(a.console,e,d),b.apply(this,arguments)}}function i(a,b,c){var d,e=b.prototype;d=a.prototype=Object.create(e),d.constructor=a,d._super=e,c&&la(d,c)}function j(a,b){return function(){return a.apply(b,arguments)}}function k(a,b){return typeof a==oa?a.apply(b?b[0]||d:d,b):a}function l(a,b){return a===d?b:a}function m(a,b,c){g(q(b),function(b){a.addEventListener(b,c,!1)})}function n(a,b,c){g(q(b),function(b){a.removeEventListener(b,c,!1)})}function o(a,b){for(;a;){if(a==b)return!0;a=a.parentNode}return!1}function p(a,b){return a.indexOf(b)>-1}function q(a){return a.trim().split(/\s+/g)}function r(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;dc[b]}):d.sort()),d}function u(a,b){for(var c,e,f=b[0].toUpperCase()+b.slice(1),g=0;g1&&!c.firstMultiple?c.firstMultiple=D(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=E(d);b.timeStamp=ra(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=I(h,i),b.distance=H(h,i),B(c,b),b.offsetDirection=G(b.deltaX,b.deltaY);var j=F(b.deltaTime,b.deltaX,b.deltaY);b.overallVelocityX=j.x,b.overallVelocityY=j.y,b.overallVelocity=qa(j.x)>qa(j.y)?j.x:j.y,b.scale=g?K(g.pointers,d):1,b.rotation=g?J(g.pointers,d):0,b.maxPointers=c.prevInput?b.pointers.length>c.prevInput.maxPointers?b.pointers.length:c.prevInput.maxPointers:b.pointers.length,C(c,b);var k=a.element;o(b.srcEvent.target,k)&&(k=b.srcEvent.target),b.target=k}function B(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};b.eventType!==Ea&&f.eventType!==Ga||(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function C(a,b){var c,e,f,g,h=a.lastInterval||b,i=b.timeStamp-h.timeStamp;if(b.eventType!=Ha&&(i>Da||h.velocity===d)){var j=b.deltaX-h.deltaX,k=b.deltaY-h.deltaY,l=F(i,j,k);e=l.x,f=l.y,c=qa(l.x)>qa(l.y)?l.x:l.y,g=G(j,k),a.lastInterval=b}else c=h.velocity,e=h.velocityX,f=h.velocityY,g=h.direction;b.velocity=c,b.velocityX=e,b.velocityY=f,b.direction=g}function D(a){for(var b=[],c=0;ce;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:pa(c/b),y:pa(d/b)}}function F(a,b,c){return{x:b/a||0,y:c/a||0}}function G(a,b){return a===b?Ia:qa(a)>=qa(b)?0>a?Ja:Ka:0>b?La:Ma}function H(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function I(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function J(a,b){return I(b[1],b[0],Ra)+I(a[1],a[0],Ra)}function K(a,b){return H(b[0],b[1],Ra)/H(a[0],a[1],Ra)}function L(){this.evEl=Ta,this.evWin=Ua,this.pressed=!1,x.apply(this,arguments)}function M(){this.evEl=Xa,this.evWin=Ya,x.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function N(){this.evTarget=$a,this.evWin=_a,this.started=!1,x.apply(this,arguments)}function O(a,b){var c=s(a.touches),d=s(a.changedTouches);return b&(Ga|Ha)&&(c=t(c.concat(d),"identifier",!0)),[c,d]}function P(){this.evTarget=bb,this.targetIds={},x.apply(this,arguments)}function Q(a,b){var c=s(a.touches),d=this.targetIds;if(b&(Ea|Fa)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=s(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return o(a.target,i)}),b===Ea)for(e=0;e-1&&d.splice(a,1)};setTimeout(e,cb)}}function U(a){for(var b=a.srcEvent.clientX,c=a.srcEvent.clientY,d=0;d=f&&db>=g)return!0}return!1}function V(a,b){this.manager=a,this.set(b)}function W(a){if(p(a,jb))return jb;var b=p(a,kb),c=p(a,lb);return b&&c?jb:b||c?b?kb:lb:p(a,ib)?ib:hb}function X(){if(!fb)return!1;var b={},c=a.CSS&&a.CSS.supports;return["auto","manipulation","pan-y","pan-x","pan-x pan-y","none"].forEach(function(d){b[d]=c?a.CSS.supports("touch-action",d):!0}),b}function Y(a){this.options=la({},this.defaults,a||{}),this.id=v(),this.manager=null,this.options.enable=l(this.options.enable,!0),this.state=nb,this.simultaneous={},this.requireFail=[]}function Z(a){return a&sb?"cancel":a&qb?"end":a&pb?"move":a&ob?"start":""}function $(a){return a==Ma?"down":a==La?"up":a==Ja?"left":a==Ka?"right":""}function _(a,b){var c=b.manager;return c?c.get(a):a}function aa(){Y.apply(this,arguments)}function ba(){aa.apply(this,arguments),this.pX=null,this.pY=null}function ca(){aa.apply(this,arguments)}function da(){Y.apply(this,arguments),this._timer=null,this._input=null}function ea(){aa.apply(this,arguments)}function fa(){aa.apply(this,arguments)}function ga(){Y.apply(this,arguments),this.pTime=!1,this.pCenter=!1,this._timer=null,this._input=null,this.count=0}function ha(a,b){return b=b||{},b.recognizers=l(b.recognizers,ha.defaults.preset),new ia(a,b)}function ia(a,b){this.options=la({},ha.defaults,b||{}),this.options.inputTarget=this.options.inputTarget||a,this.handlers={},this.session={},this.recognizers=[],this.oldCssProps={},this.element=a,this.input=y(this),this.touchAction=new V(this,this.options.touchAction),ja(this,!0),g(this.options.recognizers,function(a){var b=this.add(new a[0](a[1]));a[2]&&b.recognizeWith(a[2]),a[3]&&b.requireFailure(a[3])},this)}function ja(a,b){var c=a.element;if(c.style){var d;g(a.options.cssProps,function(e,f){d=u(c.style,f),b?(a.oldCssProps[d]=c.style[d],c.style[d]=e):c.style[d]=a.oldCssProps[d]||""}),b||(a.oldCssProps={})}}function ka(a,c){var d=b.createEvent("Event");d.initEvent(a,!0,!0),d.gesture=c,c.target.dispatchEvent(d)}var la,ma=["","webkit","Moz","MS","ms","o"],na=b.createElement("div"),oa="function",pa=Math.round,qa=Math.abs,ra=Date.now;la="function"!=typeof Object.assign?function(a){if(a===d||null===a)throw new TypeError("Cannot convert undefined or null to object");for(var b=Object(a),c=1;ch&&(b.push(a),h=b.length-1):e&(Ga|Ha)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var Za={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},$a="touchstart",_a="touchstart touchmove touchend touchcancel";i(N,x,{handler:function(a){var b=Za[a.type];if(b===Ea&&(this.started=!0),this.started){var c=O.call(this,a,b);b&(Ga|Ha)&&c[0].length-c[1].length===0&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}}});var ab={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},bb="touchstart touchmove touchend touchcancel";i(P,x,{handler:function(a){var b=ab[a.type],c=Q.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}});var cb=2500,db=25;i(R,x,{handler:function(a,b,c){var d=c.pointerType==za,e=c.pointerType==Ba;if(!(e&&c.sourceCapabilities&&c.sourceCapabilities.firesTouchEvents)){if(d)S.call(this,b,c);else if(e&&U.call(this,c))return;this.callback(a,b,c)}},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var eb=u(na.style,"touchAction"),fb=eb!==d,gb="compute",hb="auto",ib="manipulation",jb="none",kb="pan-x",lb="pan-y",mb=X();V.prototype={set:function(a){a==gb&&(a=this.compute()),fb&&this.manager.element.style&&mb[a]&&(this.manager.element.style[eb]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return g(this.manager.recognizers,function(b){k(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),W(a.join(" "))},preventDefaults:function(a){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return void b.preventDefault();var d=this.actions,e=p(d,jb)&&!mb[jb],f=p(d,lb)&&!mb[lb],g=p(d,kb)&&!mb[kb];if(e){var h=1===a.pointers.length,i=a.distance<2,j=a.deltaTime<250;if(h&&i&&j)return}return g&&f?void 0:e||f&&c&Na||g&&c&Oa?this.preventSrc(b):void 0},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var nb=1,ob=2,pb=4,qb=8,rb=qb,sb=16,tb=32;Y.prototype={defaults:{},set:function(a){return la(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(f(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=_(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return f(a,"dropRecognizeWith",this)?this:(a=_(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(f(a,"requireFailure",this))return this;var b=this.requireFail;return a=_(a,this),-1===r(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(f(a,"dropRequireFailure",this))return this;a=_(a,this);var b=r(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function b(b){c.manager.emit(b,a)}var c=this,d=this.state;qb>d&&b(c.options.event+Z(d)),b(c.options.event),a.additionalEvent&&b(a.additionalEvent),d>=qb&&b(c.options.event+Z(d))},tryEmit:function(a){return this.canEmit()?this.emit(a):void(this.state=tb)},canEmit:function(){for(var a=0;af?Ja:Ka,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?Ia:0>g?La:Ma,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return aa.prototype.attrTest.call(this,a)&&(this.state&ob||!(this.state&ob)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=$(a.direction);b&&(a.additionalEvent=this.options.event+b),this._super.emit.call(this,a)}}),i(ca,aa,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&ob)},emit:function(a){if(1!==a.scale){var b=a.scale<1?"in":"out";a.additionalEvent=this.options.event+b}this._super.emit.call(this,a)}}),i(da,Y,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[hb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distanceb.time;if(this._input=a,!d||!c||a.eventType&(Ga|Ha)&&!f)this.reset();else if(a.eventType&Ea)this.reset(),this._timer=e(function(){this.state=rb,this.tryEmit()},b.time,this);else if(a.eventType&Ga)return rb;return tb},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===rb&&(a&&a.eventType&Ga?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=ra(),this.manager.emit(this.options.event,this._input)))}}),i(ea,aa,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&ob)}}),i(fa,aa,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:Na|Oa,pointers:1},getTouchAction:function(){return ba.prototype.getTouchAction.call(this)},attrTest:function(a){var b,c=this.options.direction;return c&(Na|Oa)?b=a.overallVelocity:c&Na?b=a.overallVelocityX:c&Oa&&(b=a.overallVelocityY),this._super.attrTest.call(this,a)&&c&a.offsetDirection&&a.distance>this.options.threshold&&a.maxPointers==this.options.pointers&&qa(b)>this.options.velocity&&a.eventType&Ga},emit:function(a){var b=$(a.offsetDirection);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),i(ga,Y,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[ib]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance