├── .gitignore ├── README.md ├── config ├── config.ini ├── level1 │ └── statement_en.html ├── level2 │ ├── recipe_example.png │ ├── statement_en.html │ ├── welcome_en.html │ └── welcome_fr.html ├── level3 │ ├── statement_en.html │ ├── tomeLearn.png │ ├── welcome_en.html │ └── welcome_fr.html ├── level4 │ └── statement_en.html ├── statement_en.html.tpl ├── statement_fr.html.tpl └── stub.txt ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── codingame │ │ ├── game │ │ ├── CommandManager.java │ │ ├── Deck.java │ │ ├── DeliveryCompletion.java │ │ ├── Game.java │ │ ├── GameException.java │ │ ├── GameSummaryManager.java │ │ ├── InvalidInputException.java │ │ ├── Player.java │ │ ├── Recipe.java │ │ ├── Referee.java │ │ ├── action │ │ │ ├── Action.java │ │ │ ├── ResetAction.java │ │ │ ├── SpellAction.java │ │ │ └── WaitAction.java │ │ └── spell │ │ │ ├── DeliverySpell.java │ │ │ ├── PlayerSpell.java │ │ │ ├── Spell.java │ │ │ ├── SpellType.java │ │ │ └── TomeSpell.java │ │ └── view │ │ ├── AnimationData.java │ │ ├── BonusData.java │ │ ├── EventData.java │ │ ├── FrameViewData.java │ │ ├── GlobalViewData.java │ │ ├── Serializer.java │ │ ├── SpellData.java │ │ └── ViewModule.java └── resources │ └── view │ ├── assets │ ├── Background.jpg │ ├── cauldrons_front.png │ ├── dial0.png │ ├── goop.json │ ├── goop.png │ ├── potioff.json │ ├── potioff.png │ ├── potion.json │ ├── potion.png │ ├── read_blink_0.json │ ├── read_blink_0.png │ ├── read_blink_1.json │ ├── read_blink_1.png │ ├── rest_0.json │ ├── rest_0.png │ ├── rest_1.json │ ├── rest_1.png │ ├── rupee.json │ ├── rupee.png │ ├── sprites.json │ ├── spritesheet.png │ ├── stir_0.json │ ├── stir_0.png │ ├── stir_1.json │ └── stir_1.png │ ├── config.js │ ├── demo.js │ ├── graphics │ ├── Deserializer.js │ ├── ViewModule.js │ ├── assetConstants.js │ ├── gameConstants.js │ ├── inventoryUpdate.js │ ├── layers.js │ ├── miscUpdate.js │ ├── spellUpdate.js │ ├── utils.js │ └── witchUpdate.js │ └── tooltip │ └── TooltipModule.js └── test ├── java ├── BasicAgent.java └── Fall2020Main.java └── resources └── log4j2.properties /.gitignore: -------------------------------------------------------------------------------- 1 | .factorypath 2 | .pydevproject 3 | /.classpath 4 | /.project 5 | /.settings/ 6 | /bin/ 7 | /target/ 8 | src/main/resources/view/.eslintrc.js 9 | src/main/resources/view/node_modules/ 10 | src/main/resources/view/package-lock.json 11 | src/main/resources/view/package.json 12 | src/main/resources/view/tsconfig.json 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FallChallenge2020 2 | Source code for CodinGame's Fall Challenge 2020 event. 3 | -------------------------------------------------------------------------------- /config/config.ini: -------------------------------------------------------------------------------- 1 | min_players=2 2 | max_players=2 3 | type=multi -------------------------------------------------------------------------------- /config/level1/statement_en.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
12 |
13 | 14 |
15 | 16 |

17 | This is a league based challenge. 18 |

19 | 20 | For this challenge, multiple leagues for the same game are available. Once you have proven your skills against the 21 | first Boss, you will access a higher league and extra rules will be available. 22 | 23 |
24 | 25 |
26 |

27 |   28 | Goal 29 |

30 |
31 |
32 | End the game with more rupees than your opponent. 33 |
34 |
35 | Earn more rupees than your opponent! 37 |
38 |
39 |

40 | The game takes place in a potion shop, in which two twin-sister witches are trying to prove they 41 | are the better potion brewer.
42 | They have set up a contest: make more money selling potions than your sister.
43 | However, their witch's hut is quite small, so they must share the same space, and deal with 44 | the same client orders. 45 |

46 |
47 | 48 |
49 |

50 |   51 | Rules 52 |

53 | 54 |
55 | 56 | 57 |

58 | Each player controls a witch, each witch has access to their own inventory of potion ingredients.

59 |

Each client order is a list of ingredients required to brew a potion and 60 | earn some rupees.

61 | 62 |

63 | The game is played over several rounds. Each player performs one action each turn, simultaneously. 64 |

65 | 66 |

71 | Ingredients

72 | 73 |

74 | There are 4 tiers of ingredient, labelled 0,1,2, and 3 75 | 76 | . 77 |

78 | 79 | 80 |
81 | 83 |
Higher tier ingredients are typically necessary in more expensive potion recipes but take longer to 84 | acquire.
85 |
86 | 87 | 88 |

89 | Each witch starts with a full inventory of 10 ingredients. 90 |

91 | 92 |

The inventory is represented by inv: 4 numbers each representing the amount of each ingredient tier.

93 | 94 | 95 | 96 |

101 | Action overview

102 | 103 | 104 | 105 |

For this league, you must Brew two potions from the list of 106 | client orders. The witch 107 | having earned the most rupees wins.

108 | 109 | 110 | 111 | 112 | 113 | 114 |

119 | Brewing

120 | 121 |

Client orders are represented by delta: 4 numbers, each representing the amount of each ingredient tier needed to brew a potion. The numbers are all negative because they represent a loss of ingredients from your inventory.

122 |

For instance, a client order with delta= [-2, -1, 0, 0] means you have to consume 2 tier-0 ingredients and 1 tier-2 ingredients from your inventory in order to brew the potion.

123 |

The price of the client order is the amount of rupees will you earn by completing it.

124 | 125 | 126 | 127 |
128 | 129 | 131 |
This potion requires five ingredients and is worth 10 rupees. delta0 is -2, 132 | so you need 2 tier-0 ingredients. Check your inv0 variable.
133 |
134 | 135 |

The client orders are queued up from left to right. Only five clients can fit 136 | inside the hut so a maximum of 137 | 5 orders will be available every turn.

138 | 139 | 140 | If both witches brew the same potion, they both earn its price in rupees. 141 | 142 |

At the start of each new turn, new orders are queued up to fill the missing 143 | spaces.

144 | 145 | 146 |

147 | Each order has a unique id and can be undertaken with the ACTION id command. 148 |

149 | 150 | 151 |

You may also opt to skip a turn with the WAIT command.

152 | 153 |

158 | ⛔ Game end

159 | 160 |

161 | The game ends once at least one witch has brewed 2 potions. 162 |
163 |
164 | The game stops automatically after 100 rounds.
165 |
166 |

167 | 168 | 169 | 170 | 171 |
172 |
173 |
174 |
Victory Conditions
175 |
176 |
    177 | The winner is the player with the most rupees. 178 |
179 |
180 |
181 |
182 | 183 |
184 |
185 |
186 |
Defeat Conditions
187 |
188 |
    189 | Your program does not provide a command in the alloted time or one of the commands is unrecognized. 190 |
191 |
192 |
193 |
194 |
195 |

200 | 🐞 Debugging tips

201 |
    202 |
  • Hover over a spell or recipe to see extra information about it
  • 203 |
  • Append text after any command and that text will appear above your witch
  • 204 |
  • Press the gear icon on the viewer to access extra display options
  • 205 |
  • Use the keyboard to control the action: space to play/pause, arrows to step 1 frame at a time
  • 206 |
207 | 208 |
209 |
210 | 211 | 212 | 213 | 214 |
215 |

216 |   217 | Game Input 218 |

219 | 220 | 221 |
222 |
Input for One Game Turn
223 |
224 | Line 1: one integer actionCount for the sum total of all 225 | available tome spells, both sets of player spells, and every potion recipe.
226 | Next actionCount lines: 227 | 11 space-separated values to describe a possible action.
228 |
    229 |
  • actionIdthe id of this action
  • 230 |
  • actionType: a string
      231 | 232 |
    • 233 | BREW for a potion recipe 234 |
    • 235 |
    236 |
  • 237 |
  • delta0, delta1, delta2, delta3: the four numbers describing 238 | the input or output of ingredients for each ingredient tier.
  • 239 |
  • price the amount of rupees this will win you if this is a potion recipe, 0 240 | otherwise.
  • 241 | 242 |
  • tomeIndex: ignore for this league.
  • 243 |
  • taxCount: ignore for this league.
  • 244 |
  • castable: ignore for this league.
  • 245 |
  • repeatable: ignore for this league.
  • 246 | 247 | 248 |
249 | Next 2 lines: 250 | 5 integers to describe each player, your data is always first: 251 |
    252 |
  • inv0 for the amount of tier-0 ingredients in their inventory
  • 253 |
  • inv1 for the amount of tier-1 ingredients in their inventory
  • 254 |
  • inv2 for the amount of tier-2 ingredients in their inventory
  • 255 |
  • inv3 for the amount of tier-3 ingredients in their inventory
  • 256 |
  • score for the amount of rupees earned so far
  • 257 |
258 |
259 |
260 | 261 | 262 |
263 |
Output
264 |
265 | A single line with your action: 266 | 267 |
    268 |
  • 269 | ACTION id: your witch brews the potion with the given action id if she has the 270 | required ingredients. 271 |
  • 272 | 273 |
  • 274 | WAIT: your witch does nothing. 275 |
  • 276 |
277 |
278 |
279 | 280 |
281 |
Constraints
282 |
283 | 0 < actionCount100
284 | 6price23
285 | Response time per turn ≤ 50ms 286 |
Response time for the first turn ≤ 1000ms 287 |
288 |
289 |
290 | 291 | 292 |
297 |
299 | 300 |

What is in store for me in the higher leagues ? 301 |

302 | The extra rules available in higher leagues are: 306 |
307 | 308 |
309 | -------------------------------------------------------------------------------- /config/level2/recipe_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/config/level2/recipe_example.png -------------------------------------------------------------------------------- /config/level2/statement_en.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
12 |
13 | 14 |
15 | 16 |

17 | Summary of new rules 18 |

19 | 20 | You can now cast spells to acquire new ingredients!
21 |
See the updated statement for details.
22 |
23 | 24 |
25 |

26 |   27 | Goal 28 |

29 |
30 |
31 | End the game with more rupees than your opponent. 32 |
33 |
34 | Earn more rupees than your opponent! 36 |
37 |
38 |

39 | The game takes place in a potion shop, in which two twin-sister witches are trying to prove they 40 | are the better potion brewer.
41 | They have set up a contest: make more money selling potions than your sister.
42 | However, their witch's hut is quite small, so they must share the same space, and deal with 43 | the same client orders. 44 |

45 |
46 | 47 |
48 |

49 |   50 | Rules 51 |

52 | 53 |
54 | 55 | 56 | 57 |
58 |

59 | Each player controls a witch, each witch has access to their own inventory of potion ingredients 60 | and a list of spells they have learnt. These spells can be used to turn a certain set of ingredients 61 | into another.
62 | Each client order is a list of ingredients required to brew a potion and earn some rupees. 63 |

64 |
65 |

66 | The game is played over several rounds. Each player performs one action each turn, simultaneously. 67 |

68 | 69 |

74 | Ingredients

75 | 76 |

77 | There are 4 tiers of ingredient, labelled 0,1,2, and 3 78 | 79 | . 80 |

81 | 82 | 83 |
84 | 86 |
Higher tier ingredients are typically necessary in more expensive potion recipes but take longer to 87 | acquire.
88 |
89 | 90 | 91 |
92 |

93 | A witch's inventory can contain up to 10 ingredients. 94 |

95 |

96 | Each witch starts with 3 tier-0 ingredients in their inventory. 97 |

98 |
99 | 100 |

The inventory is represented by inv: 4 numbers each representing the amount of each ingredient tier.

101 | 102 | 103 | 104 |

109 | Action overview

110 | 111 | 112 | 113 | 114 |

Each round, you can perform one of the following actions:

115 |
116 |
    117 |
  • 118 | Cast a spell: performs one of the spells you have learnt. 119 |
  • 120 |
  • 121 | Rest to refresh all previously cast spells. 122 |
  • 123 |
  • 124 | Brew a potion to score points. 125 |
  • 126 |
127 |
128 |

You may also opt to skip a turn with the WAIT command.

129 | 130 | 131 |
132 |

137 | Casting Spells

138 | 139 | 140 |

Spells are represented by delta: 4 numbers, each representing 141 | the input (negative 142 | number) or output (positive 143 | number) for each ingredient tier. For instance, a spell marked -1,1,0,0 means it can turn one 144 | tier-0 ingredient into a tier-1 ingredient.

145 | 146 | 147 |
148 | 149 | 151 |
This spell creates a tier-3 ingredient out of a tier-0 and 152 | tier-1 ingredient.
153 |
154 | 155 | 156 | 157 |

Some spells have no input, they simply produce new ingredients.

158 | 159 |

160 | Each player spell has a unique id and can be cast with the ACTION id command.

161 |

162 | 163 |
164 |
165 | 166 | 167 |
168 |

173 | Resting

174 | 175 |

Resting lets you channel your magic, rendering all exhausted spells 176 | available again.

177 | 178 |

179 | You can order your witch to rest with the REST command. 180 |

181 | 182 | 183 |
184 | 185 |

190 | Brewing

191 | 192 |

Client orders are represented by delta: 4 numbers, each representing the amount of each ingredient tier needed to brew a potion. The numbers are all negative because they represent a loss of ingredients from your inventory.

193 |

For instance, a client order with delta= [-2, -1, 0, 0] means you have to consume 2 tier-0 ingredients and 1 tier-2 ingredients from your inventory in order to brew the potion.

194 |

The price of the client order is the amount of rupees will you earn by completing it.

195 | 196 | 197 | 198 |
199 | 200 | 202 |
This potion requires five ingredients and is worth 10 rupees. delta0 is -2, 203 | so you need 2 tier-0 ingredients. Check your inv0 variable.
204 |
205 | 206 |

The client orders are queued up from left to right. Only five clients can fit 207 | inside the hut so a maximum of 208 | 5 orders will be available every turn.

209 | 210 | 211 | If both witches brew the same potion, they both earn its price in rupees. 212 | 213 |

At the start of each new turn, new orders are queued up to fill the missing 214 | spaces.

215 | 216 | 217 |

218 | Each order has a unique id and can be undertaken with the ACTION id command. 219 |

220 | 221 | 222 | 223 |

228 | ⛔ Game end

229 | 230 |

231 |

232 | The game ends once at least one witch has brewed 3 potions. 233 |
234 |
235 |
236 | The game stops automatically after 100 rounds.
237 |
238 |

239 | 240 | 241 |
242 |

Players gain 1 rupee for each tier-1 ingredient or higher in their 243 | inventory.

244 |
245 | 246 | 247 |
248 |
249 |
250 |
Victory Conditions
251 |
252 |
    253 | The winner is the player with the most rupees. 254 |
255 |
256 |
257 |
258 | 259 |
260 |
261 |
262 |
Defeat Conditions
263 |
264 |
    265 | Your program does not provide a command in the alloted time or one of the commands is unrecognized. 266 |
267 |
268 |
269 |
270 |
271 |

276 | 🐞 Debugging tips

277 |
    278 |
  • Hover over a spell or recipe to see extra information about it
  • 279 |
  • Append text after any command and that text will appear above your witch
  • 280 |
  • Press the gear icon on the viewer to access extra display options
  • 281 |
  • Use the keyboard to control the action: space to play/pause, arrows to step 1 frame at a time
  • 282 |
283 | 284 |
285 |
286 | 287 | 288 | 289 | 290 |
291 |

292 |   293 | Game Input 294 |

295 | 296 | 297 |
298 |
Input for One Game Turn
299 |
300 | Line 1: one integer actionCount for the sum total of all 301 | available tome spells, both sets of player spells, and every potion recipe.
302 | Next actionCount lines: 303 | 11 space-separated values to describe a possible action.
304 |
305 |
    306 |
  • actionIdthe id of this action
  • 307 |
  • actionType: a string
      308 |
    • 309 | MY_SPELL for one of your learnt spells 310 |
    • 311 |
    • 312 | OPPONENT_SPELL for one of your opponent's learnt spells 313 |
    • 314 | 315 |
    • 316 | BREW for a potion recipe 317 |
    • 318 |
    319 |
  • 320 |
  • delta0, delta1, delta2, delta3: the four numbers describing 321 | the input or output of ingredients for each ingredient tier.
  • 322 |
  • price the amount of rupees this will win you if this is a potion recipe, 0 323 | otherwise.
  • 324 | 325 |
  • tomeIndex: ignore for this league.
  • 326 |
  • taxCount: ignore for this league.
  • 327 |
  • repeatable: ignore for this league.
  • 328 | 329 | 330 |
  • castable 331 | 1 if this is a player spell that is not exhausted, 0 otherwise. 332 |
  • 333 | 334 |
335 |
336 | Next 2 lines: 337 | 5 integers to describe each player, your data is always first: 338 |
    339 |
  • inv0 for the amount of tier-0 ingredients in their inventory
  • 340 |
  • inv1 for the amount of tier-1 ingredients in their inventory
  • 341 |
  • inv2 for the amount of tier-2 ingredients in their inventory
  • 342 |
  • inv3 for the amount of tier-3 ingredients in their inventory
  • 343 |
  • score for the amount of rupees earned so far
  • 344 |
345 |
346 |
347 | 348 | 349 |
350 |
Output
351 |
352 | A single line with your action: 353 | 354 |
355 |
    356 |
  • 357 | ACTION id: your witch casts or brews if the requirements are met for the given 358 | action id. 359 |
  • 360 | 361 |
  • 362 | REST: your witch channels her magic and all your exhausted spells will become castable 363 | again. 364 |
  • 365 |
  • 366 | WAIT: your witch does nothing. 367 |
  • 368 |
369 |
370 |
371 |
372 | 373 |
374 |
Constraints
375 |
376 | 0 < actionCount100
377 | 6price23
378 | Response time per turn ≤ 50ms 379 |
Response time for the first turn ≤ 1000ms 380 |
381 |
382 |
383 | 384 | 385 |
390 |
392 | 393 |

What is in store for me in the higher leagues ? 394 |

395 | The extra rules available in higher leagues are: 398 |
399 | 400 |
401 | -------------------------------------------------------------------------------- /config/level2/welcome_en.html: -------------------------------------------------------------------------------- 1 |
2 |

You've made it to the next league.

3 | You can now cast spells to transform a set of ingredients into another. 4 |
5 |
See the updated statement for details.
6 |
-------------------------------------------------------------------------------- /config/level2/welcome_fr.html: -------------------------------------------------------------------------------- 1 |
2 |

Vous venez d'atteindre la ligue supérieure.

3 | Vous pouvez désormais lancer des sorts pour transformer un ensemble d'ingrédients en un autre. 4 |
5 |
Consultez l'énoncé mis à jour pour plus de détails.
6 |
-------------------------------------------------------------------------------- /config/level3/tomeLearn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/config/level3/tomeLearn.png -------------------------------------------------------------------------------- /config/level3/welcome_en.html: -------------------------------------------------------------------------------- 1 |
2 |

You've made it to the next league.

3 | You can now learn new spells, some of which can be cast multiple times in the same turn. 4 |
5 |
See the updated statement for details.
6 |
-------------------------------------------------------------------------------- /config/level3/welcome_fr.html: -------------------------------------------------------------------------------- 1 |
2 |

Vous venez d'atteindre la ligue supérieure.

3 | Vous pouvez désormais apprendre de nouveaux sorts, dont certains qui peuvent être lancés plusieurs fois dans le même tour. 4 |
5 |
Consultez l'énoncé mis à jour pour plus de détails.
6 |
-------------------------------------------------------------------------------- /config/stub.txt: -------------------------------------------------------------------------------- 1 | gameloop 2 | read actionCount:int 3 | loop actionCount read actionId:int actionType:word(20) delta0:int delta1:int delta2:int delta3:int price:int tomeIndex:int taxCount:int castable:bool repeatable:bool 4 | loop 2 read inv0:int inv1:int inv2:int inv3:int score:int 5 | write BREW 0 6 | 7 | INPUT 8 | actionCount: the number of spells and recipes in play 9 | actionId: the unique ID of this spell or recipe 10 | actionType: in the first league: BREW; later: CAST, OPPONENT_CAST, LEARN, BREW 11 | delta0: tier-0 ingredient change 12 | delta1: tier-1 ingredient change 13 | delta2: tier-2 ingredient change 14 | delta3: tier-3 ingredient change 15 | price: the price in rupees if this is a potion 16 | tomeIndex: in the first two leagues: always 0; later: the index in the tome if this is a tome spell, equal to the read-ahead tax; For brews, this is the value of the current urgency bonus 17 | taxCount: in the first two leagues: always 0; later: the amount of taxed tier-0 ingredients you gain from learning this spell; For brews, this is how many times you can still gain an urgency bonus 18 | castable: in the first league: always 0; later: 1 if this is a castable player spell 19 | repeatable: for the first two leagues: always 0; later: 1 if this is a repeatable player spell 20 | inv0: tier-0 ingredients in inventory 21 | score: amount of rupees 22 | 23 | OUTPUT 24 | in the first league: BREW | WAIT; later: BREW | CAST [] | LEARN | REST | WAIT -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.codingame.game 7 | fall-2020 8 | 1.0-SNAPSHOT 9 | 10 | 11 | 3.15.4 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | com.codingame.gameengine 19 | core 20 | ${gamengine.version} 21 | 22 | 23 | 24 | com.codingame.gameengine 25 | runner 26 | ${gamengine.version} 27 | 28 | 29 | 30 | com.codingame.gameengine 31 | module-entities 32 | ${gamengine.version} 33 | 34 | 35 | 36 | com.codingame.gameengine 37 | module-toggle 38 | ${gamengine.version} 39 | 40 | 41 | 42 | com.codingame.gameengine 43 | module-endscreen 44 | ${gamengine.version} 45 | 46 | 47 | 48 | com.codingame.gameengine 49 | module-endscreen 50 | ${gamengine.version} 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/CommandManager.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | import com.codingame.game.action.ResetAction; 9 | import com.codingame.game.action.SpellAction; 10 | import com.codingame.game.action.WaitAction; 11 | import com.google.inject.Inject; 12 | import com.google.inject.Singleton; 13 | 14 | @Singleton 15 | public class CommandManager { 16 | @Inject private GameSummaryManager gameSummaryManager; 17 | 18 | static final Pattern PLAYER_LEARN_PATTERN = Pattern.compile( 19 | "^LEARN\\s+(?\\d+)(?:\\s+(?.*))?", 20 | Pattern.CASE_INSENSITIVE 21 | ); 22 | static final Pattern PLAYER_BREW_PATTERN = Pattern.compile( 23 | "^BREW\\s+(?\\d+)(?:\\s+(?.*))?", 24 | Pattern.CASE_INSENSITIVE 25 | ); 26 | static final Pattern PLAYER_CAST_PATTERN = Pattern.compile( 27 | "^CAST\\s+(?\\d+)(?:\\s+(?\\d+))?(?:\\s+(?.*))?", 28 | Pattern.CASE_INSENSITIVE 29 | ); 30 | 31 | static final Pattern PLAYER_RESET_PATTERN = Pattern.compile( 32 | "^REST(?:\\s+(?.*))?", 33 | Pattern.CASE_INSENSITIVE 34 | ); 35 | static final Pattern PLAYER_WAIT_PATTERN = Pattern.compile( 36 | "^WAIT(?:\\s+(?.*))?", 37 | Pattern.CASE_INSENSITIVE 38 | ); 39 | 40 | public void parseCommands(Player player, List lines, Game game) { 41 | String command = lines.get(0); 42 | 43 | try { 44 | Matcher match; 45 | 46 | if (Game.ACTIVE_SPELLS) { 47 | match = PLAYER_RESET_PATTERN.matcher(command); 48 | if (match.matches()) { 49 | player.setAction(new ResetAction()); 50 | matchMessage(player, match); 51 | return; 52 | } 53 | 54 | match = PLAYER_CAST_PATTERN.matcher(command); 55 | if (match.matches()) { 56 | int spellId = Integer.valueOf(match.group("id")); 57 | Optional param = Optional.ofNullable(match.group("param")).map(Integer::valueOf); 58 | player.setAction(new SpellAction("CAST", spellId, param)); 59 | matchMessage(player, match); 60 | return; 61 | } 62 | } 63 | if (Game.ACTIVE_TOME) { 64 | match = PLAYER_LEARN_PATTERN.matcher(command); 65 | if (match.matches()) { 66 | int spellId = Integer.valueOf(match.group("id")); 67 | player.setAction(new SpellAction("LEARN", spellId, Optional.empty())); 68 | matchMessage(player, match); 69 | return; 70 | } 71 | } 72 | match = PLAYER_BREW_PATTERN.matcher(command); 73 | if (match.matches()) { 74 | int spellId = Integer.valueOf(match.group("id")); 75 | player.setAction(new SpellAction("BREW", spellId, Optional.empty())); 76 | matchMessage(player, match); 77 | return; 78 | } 79 | 80 | match = PLAYER_WAIT_PATTERN.matcher(command); 81 | if (match.matches()) { 82 | player.setAction(new WaitAction()); 83 | matchMessage(player, match); 84 | return; 85 | } 86 | 87 | throw new InvalidInputException(Game.getExpected(), command); 88 | 89 | } catch (InvalidInputException e) { 90 | deactivatePlayer(player, e.getMessage()); 91 | gameSummaryManager.addPlayerBadCommand(player, e); 92 | gameSummaryManager.addPlayerDisqualified(player); 93 | } catch (Exception e) { 94 | InvalidInputException invalidInputException = new InvalidInputException(Game.getExpected(), e.toString()); 95 | deactivatePlayer(player, invalidInputException.getMessage()); 96 | gameSummaryManager.addPlayerBadCommand(player, invalidInputException); 97 | gameSummaryManager.addPlayerDisqualified(player); 98 | } 99 | 100 | } 101 | 102 | public void deactivatePlayer(Player player, String message) { 103 | player.deactivate(escapeHTMLEntities(message)); 104 | player.setScore(-1); 105 | } 106 | 107 | private String escapeHTMLEntities(String message) { 108 | return message 109 | .replace("<", "<") 110 | .replace(">", ">"); 111 | } 112 | 113 | private void matchMessage(Player player, Matcher match) { 114 | String message = match.group("message"); 115 | if (message != null) { 116 | if (message.length() > 48) { 117 | message = message.substring(0, 46) + "..."; 118 | } 119 | player.setMessage(message); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Deck.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.LinkedList; 4 | 5 | import com.codingame.game.spell.DeliverySpell; 6 | import com.codingame.game.spell.TomeSpell; 7 | 8 | public class Deck { 9 | 10 | public LinkedList tome = new LinkedList<>(); 11 | public LinkedList deliveries = new LinkedList<>(); 12 | 13 | public Deck() { 14 | tome.add(new TomeSpell(new Recipe(-3, 0, 0, 1))); 15 | tome.add(new TomeSpell(new Recipe(3, -1, 0, 0))); 16 | tome.add(new TomeSpell(new Recipe(1, 1, 0, 0))); 17 | tome.add(new TomeSpell(new Recipe(0, 0, 1, 0))); 18 | tome.add(new TomeSpell(new Recipe(3, 0, 0, 0))); 19 | tome.add(new TomeSpell(new Recipe(2, 3, -2, 0))); 20 | tome.add(new TomeSpell(new Recipe(2, 1, -2, 1))); 21 | tome.add(new TomeSpell(new Recipe(3, 0, 1, -1))); 22 | tome.add(new TomeSpell(new Recipe(3, -2, 1, 0))); 23 | tome.add(new TomeSpell(new Recipe(2, -3, 2, 0))); 24 | tome.add(new TomeSpell(new Recipe(2, 2, 0, -1))); 25 | tome.add(new TomeSpell(new Recipe(-4, 0, 2, 0))); 26 | tome.add(new TomeSpell(new Recipe(2, 1, 0, 0))); 27 | tome.add(new TomeSpell(new Recipe(4, 0, 0, 0))); 28 | tome.add(new TomeSpell(new Recipe(0, 0, 0, 1))); 29 | tome.add(new TomeSpell(new Recipe(0, 2, 0, 0))); 30 | tome.add(new TomeSpell(new Recipe(1, 0, 1, 0))); 31 | tome.add(new TomeSpell(new Recipe(-2, 0, 1, 0))); 32 | tome.add(new TomeSpell(new Recipe(-1, -1, 0, 1))); 33 | tome.add(new TomeSpell(new Recipe(0, 2, -1, 0))); 34 | tome.add(new TomeSpell(new Recipe(2, -2, 0, 1))); 35 | tome.add(new TomeSpell(new Recipe(-3, 1, 1, 0))); 36 | tome.add(new TomeSpell(new Recipe(0, 2, -2, 1))); 37 | tome.add(new TomeSpell(new Recipe(1, -3, 1, 1))); 38 | tome.add(new TomeSpell(new Recipe(0, 3, 0, -1))); 39 | tome.add(new TomeSpell(new Recipe(0, -3, 0, 2))); 40 | tome.add(new TomeSpell(new Recipe(1, 1, 1, -1))); 41 | tome.add(new TomeSpell(new Recipe(1, 2, -1, 0))); 42 | tome.add(new TomeSpell(new Recipe(4, 1, -1, 0))); 43 | tome.add(new TomeSpell(new Recipe(-5, 0, 0, 2))); 44 | tome.add(new TomeSpell(new Recipe(-4, 0, 1, 1))); 45 | tome.add(new TomeSpell(new Recipe(0, 3, 2, -2))); 46 | tome.add(new TomeSpell(new Recipe(1, 1, 3, -2))); 47 | tome.add(new TomeSpell(new Recipe(-5, 0, 3, 0))); 48 | tome.add(new TomeSpell(new Recipe(-2, 0, -1, 2))); 49 | tome.add(new TomeSpell(new Recipe(0, 0, -3, 3))); 50 | tome.add(new TomeSpell(new Recipe(0, -3, 3, 0))); 51 | tome.add(new TomeSpell(new Recipe(-3, 3, 0, 0))); 52 | tome.add(new TomeSpell(new Recipe(-2, 2, 0, 0))); 53 | tome.add(new TomeSpell(new Recipe(0, 0, -2, 2))); 54 | tome.add(new TomeSpell(new Recipe(0, -2, 2, 0))); 55 | tome.add(new TomeSpell(new Recipe(0, 0, 2, -1))); 56 | 57 | deliveries.add(new DeliverySpell(new Recipe(2, 2, 0, 0), 6)); 58 | deliveries.add(new DeliverySpell(new Recipe(3, 2, 0, 0), 7)); 59 | deliveries.add(new DeliverySpell(new Recipe(0, 4, 0, 0), 8)); 60 | deliveries.add(new DeliverySpell(new Recipe(2, 0, 2, 0), 8)); 61 | deliveries.add(new DeliverySpell(new Recipe(2, 3, 0, 0), 8)); 62 | deliveries.add(new DeliverySpell(new Recipe(3, 0, 2, 0), 9)); 63 | deliveries.add(new DeliverySpell(new Recipe(0, 2, 2, 0), 10)); 64 | deliveries.add(new DeliverySpell(new Recipe(0, 5, 0, 0), 10)); 65 | deliveries.add(new DeliverySpell(new Recipe(2, 0, 0, 2), 10)); 66 | deliveries.add(new DeliverySpell(new Recipe(2, 0, 3, 0), 11)); 67 | deliveries.add(new DeliverySpell(new Recipe(3, 0, 0, 2), 11)); 68 | deliveries.add(new DeliverySpell(new Recipe(0, 0, 4, 0), 12)); 69 | deliveries.add(new DeliverySpell(new Recipe(0, 2, 0, 2), 12)); 70 | deliveries.add(new DeliverySpell(new Recipe(0, 3, 2, 0), 12)); 71 | deliveries.add(new DeliverySpell(new Recipe(0, 2, 3, 0), 13)); 72 | deliveries.add(new DeliverySpell(new Recipe(0, 0, 2, 2), 14)); 73 | deliveries.add(new DeliverySpell(new Recipe(0, 3, 0, 2), 14)); 74 | deliveries.add(new DeliverySpell(new Recipe(2, 0, 0, 3), 14)); 75 | deliveries.add(new DeliverySpell(new Recipe(0, 0, 5, 0), 15)); 76 | deliveries.add(new DeliverySpell(new Recipe(0, 0, 0, 4), 16)); 77 | deliveries.add(new DeliverySpell(new Recipe(0, 2, 0, 3), 16)); 78 | deliveries.add(new DeliverySpell(new Recipe(0, 0, 3, 2), 17)); 79 | deliveries.add(new DeliverySpell(new Recipe(0, 0, 2, 3), 18)); 80 | deliveries.add(new DeliverySpell(new Recipe(0, 0, 0, 5), 20)); 81 | deliveries.add(new DeliverySpell(new Recipe(2, 1, 0, 1), 9)); 82 | deliveries.add(new DeliverySpell(new Recipe(0, 2, 1, 1), 12)); 83 | deliveries.add(new DeliverySpell(new Recipe(1, 0, 2, 1), 12)); 84 | deliveries.add(new DeliverySpell(new Recipe(2, 2, 2, 0), 13)); 85 | deliveries.add(new DeliverySpell(new Recipe(2, 2, 0, 2), 15)); 86 | deliveries.add(new DeliverySpell(new Recipe(2, 0, 2, 2), 17)); 87 | deliveries.add(new DeliverySpell(new Recipe(0, 2, 2, 2), 19)); 88 | deliveries.add(new DeliverySpell(new Recipe(1, 1, 1, 1), 12)); 89 | deliveries.add(new DeliverySpell(new Recipe(3, 1, 1, 1), 14)); 90 | deliveries.add(new DeliverySpell(new Recipe(1, 3, 1, 1), 16)); 91 | deliveries.add(new DeliverySpell(new Recipe(1, 1, 3, 1), 18)); 92 | deliveries.add(new DeliverySpell(new Recipe(1, 1, 1, 3), 20)); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/DeliveryCompletion.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import com.codingame.game.spell.DeliverySpell; 4 | 5 | public class DeliveryCompletion { 6 | private DeliverySpell delivery; 7 | private int index, earned; 8 | 9 | public DeliveryCompletion(DeliverySpell delivery, int index, int earned) { 10 | this.delivery = delivery; 11 | this.index = index; 12 | this.earned = earned; 13 | } 14 | 15 | public DeliverySpell getDelivery() { 16 | return delivery; 17 | } 18 | 19 | public int getIndex() { 20 | return index; 21 | } 22 | 23 | public int getEarned() { 24 | return earned; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/GameException.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | @SuppressWarnings("serial") 4 | public class GameException extends Exception { 5 | 6 | public GameException(String string) { 7 | super(string); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/GameSummaryManager.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | import com.codingame.game.spell.Spell; 8 | import com.codingame.gameengine.core.GameManager; 9 | import com.google.inject.Singleton; 10 | 11 | @Singleton 12 | public class GameSummaryManager { 13 | private List lines; 14 | 15 | public GameSummaryManager() { 16 | this.lines = new ArrayList<>(); 17 | } 18 | 19 | public String getSummary() { 20 | return toString(); 21 | } 22 | 23 | public void clear() { 24 | this.lines.clear(); 25 | } 26 | 27 | public void addPlayerDelivery(Player player, DeliveryCompletion delComplete) { 28 | lines.add( 29 | String.format( 30 | "%s brewed potion %d and earned %d rupees\nThey have now brewed %d potions.", 31 | player.getNicknameToken(), 32 | delComplete.getDelivery().getId(), 33 | delComplete.getEarned(), 34 | player.getDeliveriesCount() 35 | ) 36 | ); 37 | } 38 | 39 | public void addPlayerSpellLearn(Player player, Spell spell) { 40 | if (spell.getStock() > 0) { 41 | if (player.getInventory().getTotal() >= Game.MAX_SPACE) { 42 | lines.add( 43 | String.format( 44 | "%s learnt spell %d, the taxed ingredients were lost because their inventory is full.", 45 | player.getNicknameToken(), 46 | spell.getId() 47 | ) 48 | ); 49 | } else if (player.getInventory().getTotal() + spell.getStock() > Game.MAX_SPACE) { 50 | lines.add( 51 | String.format( 52 | "%s learnt spell %d and gained %d taxed ingredients, the rest was lost because their inventory is full.", 53 | player.getNicknameToken(), 54 | spell.getId(), 55 | Game.MAX_SPACE - player.getInventory().getTotal() 56 | ) 57 | ); 58 | } else { 59 | lines.add( 60 | String.format( 61 | "%s learnt spell %d and gained %d taxed ingredients.", 62 | player.getNicknameToken(), 63 | spell.getId(), 64 | spell.getStock() 65 | ) 66 | ); 67 | } 68 | } else { 69 | lines.add( 70 | String.format( 71 | "%s learnt spell %d", 72 | player.getNicknameToken(), 73 | spell.getId() 74 | ) 75 | ); 76 | } 77 | } 78 | 79 | public void addPlayerSpellAction(Player player, Spell spell) { 80 | lines.add( 81 | String.format( 82 | "%s cast spell %d", 83 | player.getNicknameToken(), 84 | spell.getId() 85 | ) 86 | ); 87 | } 88 | 89 | public void addPlayerResetAction(Player player) { 90 | lines.add( 91 | String.format( 92 | "%s rested", 93 | player.getNicknameToken() 94 | ) 95 | ); 96 | } 97 | 98 | public void addPlayerWaitAction(Player player) { 99 | lines.add( 100 | String.format( 101 | "%s bided their time", 102 | player.getNicknameToken() 103 | ) 104 | ); 105 | } 106 | 107 | public void addPlayerInvalidAction(Player player, String errorMessage) { 108 | if (player.getAction().isReset()) { 109 | lines.add( 110 | String.format( 111 | "%s attempted an invalid REST action:\n%s", 112 | player.getNicknameToken(), 113 | errorMessage 114 | ) 115 | 116 | ); 117 | } else { 118 | lines.add( 119 | String.format( 120 | "%s attempted an invalid action:\n%s", 121 | player.getNicknameToken(), 122 | errorMessage 123 | ) 124 | 125 | ); 126 | } 127 | } 128 | 129 | public void addPlayerBadCommand(Player player, InvalidInputException invalidInputException) { 130 | lines.add( 131 | GameManager.formatErrorMessage( 132 | String.format( 133 | "%s provided invalid input. Expected '%s'\nGot '%s'", 134 | player.getNicknameToken(), 135 | invalidInputException.getExpected(), 136 | invalidInputException.getGot() 137 | ) 138 | ) 139 | ); 140 | } 141 | 142 | public void addPlayerTimeout(Player player) { 143 | lines.add( 144 | GameManager.formatErrorMessage( 145 | String.format( 146 | "%s has not provided %d lines in time.", 147 | player.getNicknameToken(), 148 | player.getExpectedOutputLines() 149 | ) 150 | ) 151 | ); 152 | } 153 | 154 | public void addPlayerDisqualified(Player player) { 155 | lines.add( 156 | String.format( 157 | "%s was disqualified.", 158 | player.getNicknameToken() 159 | ) 160 | ); 161 | } 162 | 163 | @Override 164 | public String toString() { 165 | return lines.stream().collect(Collectors.joining("\n")); 166 | } 167 | } -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/InvalidInputException.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | @SuppressWarnings("serial") 4 | public class InvalidInputException extends Exception { 5 | private final String expected; 6 | private final String got; 7 | 8 | public InvalidInputException(String expected, String got) { 9 | super("Invalid Input: Expected " + expected + " but got '" + got + "'"); 10 | this.expected = expected; 11 | this.got = got; 12 | } 13 | 14 | public String getExpected() { 15 | return expected; 16 | } 17 | 18 | public String getGot() { 19 | return got; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Player.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.codingame.game.action.Action; 7 | import com.codingame.game.spell.PlayerSpell; 8 | import com.codingame.gameengine.core.AbstractMultiplayerPlayer; 9 | 10 | public class Player extends AbstractMultiplayerPlayer { 11 | private Recipe inventory = new Recipe(); 12 | private List spells = new ArrayList<>(); 13 | private Action action; 14 | private int deliveriesCount = 0; 15 | private String message; 16 | 17 | public Player() { 18 | } 19 | 20 | @Override 21 | public int getExpectedOutputLines() { 22 | return 1; 23 | } 24 | 25 | public void initSpells() { 26 | spells.add(new PlayerSpell(new Recipe(2, 0, 0, 0), this)); 27 | spells.add(new PlayerSpell(new Recipe(-1, 1, 0, 0), this)); 28 | spells.add(new PlayerSpell(new Recipe(0, -1, 1, 0), this)); 29 | spells.add(new PlayerSpell(new Recipe(0, 0, -1, 1), this)); 30 | } 31 | 32 | public void reset() { 33 | setAction(Action.NO_ACTION); 34 | message = null; 35 | } 36 | 37 | public String getMessage() { 38 | return message; 39 | } 40 | 41 | public boolean canAfford(Recipe recipe, int repeats) { 42 | for (int i = 0; i < Game.INGREDIENT_TYPE_COUNT; ++i) { 43 | if (getInventory().delta[i] + recipe.delta[i] * repeats < 0) { 44 | return false; 45 | } 46 | } 47 | return true; 48 | } 49 | 50 | public boolean enoughSpace(Recipe recipe, int repeats) { 51 | return recipe.getTotal() * repeats + getInventory().getTotal() <= Game.MAX_SPACE; 52 | } 53 | 54 | public boolean canDeliver(Recipe need) { 55 | for (int i = 0; i < Game.INGREDIENT_TYPE_COUNT; ++i) { 56 | if (getInventory().delta[i] + need.delta[i] < 0) { 57 | return false; 58 | } 59 | } 60 | return true; 61 | } 62 | 63 | public void addScore(int n) { 64 | setScore(getScore() + n); 65 | } 66 | 67 | public void addDelivery() { 68 | this.deliveriesCount++; 69 | } 70 | 71 | public int getDeliveriesCount() { 72 | return this.deliveriesCount; 73 | } 74 | 75 | public Recipe getInventory() { 76 | return inventory; 77 | } 78 | 79 | public List getSpells() { 80 | return spells; 81 | } 82 | 83 | public Action getAction() { 84 | return action; 85 | } 86 | 87 | public void setAction(Action action) { 88 | this.action = action; 89 | } 90 | 91 | public int[] getDelta() { 92 | return inventory.delta; 93 | } 94 | 95 | public void setMessage(String message) { 96 | this.message = message; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Recipe.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.Arrays; 4 | import java.util.stream.IntStream; 5 | 6 | public class Recipe { 7 | public int[] delta; 8 | 9 | public static final String[] CHARS = new String[] { "A", "B", "C", "D" }; 10 | 11 | public Recipe(int a, int b, int c, int d) { 12 | this.delta = new int[] { a, b, c, d }; 13 | } 14 | 15 | public Recipe() { 16 | this.delta = new int[] { 0, 0, 0, 0 }; 17 | } 18 | 19 | public Recipe(Recipe other) { 20 | this(other.delta[0], other.delta[1], other.delta[2], other.delta[3]); 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | StringBuffer sb = new StringBuffer(); 26 | if (IntStream.of(this.delta).allMatch(b -> b == 0)) { 27 | return "∅"; 28 | } 29 | 30 | if (IntStream.of(this.delta).anyMatch(b -> b > 0)) { 31 | sb.append("+"); 32 | for (int i = 0; i < 4; ++i) { 33 | for (int k = 0; k < this.delta[i]; ++k) { 34 | sb.append(CHARS[i]); 35 | } 36 | } 37 | } 38 | 39 | if (IntStream.of(this.delta).anyMatch(b -> b < 0)) { 40 | if (IntStream.of(this.delta).anyMatch(b -> b > 0)) { 41 | sb.append('\n'); 42 | } 43 | sb.append("-"); 44 | for (int i = 0; i < 4; ++i) { 45 | for (int k = this.delta[i]; k < 0; ++k) { 46 | sb.append(CHARS[i]); 47 | } 48 | } 49 | } 50 | 51 | return sb.toString(); 52 | } 53 | 54 | public String toPlayerString() { 55 | return String.format("%d %d %d %d", delta[0], delta[1], delta[2], delta[3]); 56 | } 57 | 58 | public void add(int idx, int x) { 59 | this.delta[idx] += x; 60 | } 61 | 62 | public int getTotal() { 63 | return IntStream.of(delta).sum(); 64 | } 65 | 66 | public int getTotalLoss() { 67 | return -IntStream.of(delta) 68 | .filter(i -> i < 0) 69 | .sum(); 70 | } 71 | 72 | public int getTotalGain() { 73 | return IntStream.of(delta) 74 | .filter(i -> i > 0) 75 | .sum(); 76 | } 77 | 78 | @Override 79 | public int hashCode() { 80 | return Arrays.hashCode(this.delta); 81 | } 82 | 83 | @Override 84 | public boolean equals(Object object) { 85 | if (this == object) { 86 | return true; 87 | } 88 | 89 | if (!(object instanceof Recipe)) { 90 | return false; 91 | } 92 | 93 | Recipe other = (Recipe) object; 94 | 95 | return Arrays.equals(this.delta, other.delta); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/Referee.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | 6 | import com.codingame.game.spell.Spell; 7 | import com.codingame.gameengine.core.AbstractPlayer.TimeoutException; 8 | import com.codingame.gameengine.core.AbstractReferee; 9 | import com.codingame.gameengine.core.MultiplayerGameManager; 10 | import com.codingame.gameengine.module.endscreen.EndScreenModule; 11 | import com.codingame.view.FrameViewData; 12 | import com.codingame.view.GlobalViewData; 13 | import com.codingame.view.SpellData; 14 | import com.codingame.view.ViewModule; 15 | import com.google.inject.Inject; 16 | import com.google.inject.Singleton; 17 | 18 | @Singleton 19 | public class Referee extends AbstractReferee { 20 | 21 | private static final int MAX_TURNS = 100; 22 | @Inject private MultiplayerGameManager gameManager; 23 | @Inject private CommandManager commandManager; 24 | @Inject private Game game; 25 | @Inject private EndScreenModule eem; 26 | @Inject private ViewModule viewModule; 27 | @Inject private GameSummaryManager gameSummaryManager; 28 | 29 | long seed; 30 | 31 | int maxFrames; 32 | 33 | @Override 34 | public void init() { 35 | viewModule.setReferee(this); 36 | this.seed = gameManager.getSeed(); 37 | 38 | maxFrames = MAX_TURNS; 39 | 40 | try { 41 | gameManager.setFrameDuration(500); 42 | gameManager.setMaxTurns(MAX_TURNS); 43 | gameManager.setTurnMaxTime(50); 44 | 45 | game.init(seed); 46 | sendGlobalInfo(); 47 | 48 | } catch (Exception e) { 49 | e.printStackTrace(); 50 | System.err.println("Referee failed to initialize"); 51 | abort(); 52 | } 53 | } 54 | 55 | private void abort() { 56 | System.err.println("Unexpected game end"); 57 | gameManager.endGame(); 58 | } 59 | 60 | private void sendGlobalInfo() { 61 | for (Player player : gameManager.getActivePlayers()) { 62 | for (String line : game.getGlobalInfoFor(player)) { 63 | player.sendInputLine(line); 64 | } 65 | } 66 | } 67 | 68 | @Override 69 | public void gameTurn(int turn) { 70 | game.resetGameTurnData(); 71 | 72 | // Give input to players 73 | for (Player player : gameManager.getActivePlayers()) { 74 | for (String line : game.getCurrentFrameInfoFor(player)) { 75 | player.sendInputLine(line); 76 | } 77 | player.execute(); 78 | } 79 | // Get output from players 80 | handlePlayerCommands(); 81 | 82 | game.performGameUpdate(); 83 | } 84 | 85 | private void handlePlayerCommands() { 86 | for (Player player : gameManager.getActivePlayers()) { 87 | try { 88 | commandManager.parseCommands(player, player.getOutputs(), game); 89 | } catch (TimeoutException e) { 90 | commandManager.deactivatePlayer(player, "Timeout!"); 91 | gameSummaryManager.addPlayerTimeout(player); 92 | gameSummaryManager.addPlayerDisqualified(player); 93 | } 94 | } 95 | 96 | } 97 | 98 | @Override 99 | public void onEnd() { 100 | eem.setTitleRankingsSprite("logo.png"); 101 | 102 | game.onEnd(); 103 | 104 | int scores[] = gameManager.getPlayers().stream() 105 | .mapToInt(Player::getScore) 106 | .toArray(); 107 | 108 | eem.setScores(scores); 109 | } 110 | 111 | private List getPlayerSpellData(Player p) { 112 | return p.getSpells().stream() 113 | .map( 114 | spell -> new SpellData(spell.getId(), spell.getDelta(), spell.isRepeatable()) 115 | ) 116 | .collect(Collectors.toList()); 117 | } 118 | 119 | public FrameViewData getCurrentFrameData() { 120 | FrameViewData data = new FrameViewData(); 121 | data.events = game.getViewerEvents(); 122 | data.scores = gameManager.getPlayers().stream() 123 | .map(Player::getScore) 124 | .collect(Collectors.toList()); 125 | 126 | data.playerSpells = gameManager.getPlayers().stream() 127 | .map( 128 | player -> player.getSpells().stream() 129 | .map(Spell::getId) 130 | .collect(Collectors.toList()) 131 | ) 132 | .collect(Collectors.toList()); 133 | 134 | data.tomeSpells = game.getTome().stream() 135 | .map(Spell::getId) 136 | .collect(Collectors.toList()); 137 | 138 | data.deliveries = game.getDeliveries().stream() 139 | .map(Spell::getId) 140 | .collect(Collectors.toList()); 141 | 142 | data.inventories = gameManager.getPlayers().stream() 143 | .map(Player::getDelta) 144 | .collect(Collectors.toList()); 145 | 146 | data.active = gameManager.getPlayers().stream() 147 | .flatMap(p -> p.getSpells().stream()) 148 | .filter(Spell::isActive) 149 | .map(Spell::getId) 150 | .collect(Collectors.toList()); 151 | 152 | data.stock = game.getTome().stream() 153 | .filter(sp -> sp.getStock() > 0) 154 | .collect( 155 | Collectors.toMap( 156 | Spell::getId, Spell::getStock 157 | ) 158 | ); 159 | 160 | data.bonus = game.getBonusData(); 161 | data.messages = gameManager.getPlayers().stream() 162 | .filter(player -> player.getMessage() != null) 163 | .collect(Collectors.toMap(Player::getIndex, Player::getMessage)); 164 | 165 | return data; 166 | } 167 | 168 | public GlobalViewData getGlobalData() { 169 | GlobalViewData data = new GlobalViewData(); 170 | 171 | data.playerSpells = gameManager.getPlayers().stream() 172 | .map(this::getPlayerSpellData) 173 | .collect(Collectors.toList()); 174 | 175 | data.tomeSpells = game.getTome().stream() 176 | .map( 177 | spell -> new SpellData(spell.getId(), spell.getDelta(), spell.isRepeatable()) 178 | ) 179 | .collect(Collectors.toList()); 180 | 181 | data.deliveries = game.getDeliveries().stream() 182 | .map( 183 | spell -> new SpellData(spell.getId(), spell.getDelta(), spell.getScore()) 184 | ) 185 | .collect(Collectors.toList()); 186 | return data; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/action/Action.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.action; 2 | 3 | public abstract class Action { 4 | public int spellId; 5 | public String str; 6 | 7 | public static final Action NO_ACTION = new Action() { 8 | }; 9 | 10 | public Action() { 11 | this.str = "NO_ACTION"; 12 | } 13 | 14 | public String getStr() { 15 | return str; 16 | } 17 | 18 | public boolean isSpell() { 19 | return false; 20 | } 21 | 22 | public boolean isReset() { 23 | return false; 24 | } 25 | 26 | public boolean isWait() { 27 | return false; 28 | } 29 | 30 | public int getRepeats() { 31 | return 1; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/action/ResetAction.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.action; 2 | 3 | public class ResetAction extends Action { 4 | 5 | public boolean isReset() { 6 | return true; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/action/SpellAction.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.action; 2 | 3 | import java.util.Optional; 4 | 5 | public class SpellAction extends Action { 6 | 7 | private Optional param; 8 | 9 | public SpellAction(String str, int spellId, Optional param) { 10 | this.str = str; 11 | this.spellId = spellId; 12 | this.param = param; 13 | } 14 | 15 | @Override 16 | public boolean isSpell() { 17 | return true; 18 | } 19 | 20 | @Override 21 | public int getRepeats() { 22 | return param.orElse(1); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/action/WaitAction.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.action; 2 | 3 | public class WaitAction extends Action { 4 | 5 | @Override 6 | public boolean isWait() { 7 | return true; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/spell/DeliverySpell.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.spell; 2 | 3 | import com.codingame.game.Recipe; 4 | 5 | public class DeliverySpell extends Spell { 6 | private int score; 7 | 8 | public DeliverySpell(Recipe need, int score) { 9 | this.recipe = new Recipe(-need.delta[0], -need.delta[1], -need.delta[2], -need.delta[3]); 10 | this.score = score; 11 | } 12 | 13 | @Override 14 | public int getScore() { 15 | return score; 16 | } 17 | 18 | @Override 19 | public boolean isRepeatable() { 20 | return false; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/spell/PlayerSpell.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.spell; 2 | 3 | import com.codingame.game.Player; 4 | import com.codingame.game.Recipe; 5 | 6 | public class PlayerSpell extends Spell { 7 | 8 | private boolean active; 9 | private boolean repeatable; 10 | public Player owner; 11 | 12 | private PlayerSpell(Recipe recipe, Player owner, boolean repeatable) { 13 | this.recipe = recipe; 14 | this.active = true; 15 | this.owner = owner; 16 | this.repeatable = repeatable; 17 | } 18 | 19 | public PlayerSpell(Recipe recipe, Player owner) { 20 | this(recipe, owner, false); 21 | } 22 | 23 | public PlayerSpell(TomeSpell learnt, Player owner) { 24 | this(new Recipe(learnt.recipe), owner, learnt.isRepeatable()); 25 | } 26 | 27 | @Override 28 | public boolean isActive() { 29 | return active; 30 | } 31 | 32 | @Override 33 | public boolean isOwner(Player player) { 34 | return player == owner; 35 | } 36 | 37 | @Override 38 | public boolean isRepeatable() { 39 | return repeatable; 40 | } 41 | 42 | public void activate() { 43 | active = true; 44 | } 45 | 46 | public void deactivate() { 47 | active = false; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/spell/Spell.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.spell; 2 | 3 | import com.codingame.game.Player; 4 | import com.codingame.game.Recipe; 5 | 6 | public abstract class Spell { 7 | private static int INSTANCE_COUNT = 0; 8 | 9 | public Recipe recipe; 10 | private int id = INSTANCE_COUNT++; 11 | 12 | public int getId() { 13 | return id; 14 | } 15 | 16 | public int getScore() { 17 | return 0; 18 | } 19 | 20 | public int getStock() { 21 | return -1; 22 | } 23 | 24 | public boolean isActive() { 25 | return false; 26 | } 27 | 28 | public boolean isOwner(Player player) { 29 | return false; 30 | } 31 | 32 | public int[] getDelta() { 33 | return recipe.delta; 34 | } 35 | 36 | public abstract boolean isRepeatable(); 37 | 38 | @Override 39 | public String toString() { 40 | return String.valueOf(id); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/spell/SpellType.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.spell; 2 | 3 | public enum SpellType { 4 | CAST, 5 | OPPONENT_CAST, 6 | LEARN, 7 | BREW 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/game/spell/TomeSpell.java: -------------------------------------------------------------------------------- 1 | package com.codingame.game.spell; 2 | 3 | import java.util.stream.IntStream; 4 | 5 | import com.codingame.game.Recipe; 6 | 7 | public class TomeSpell extends Spell { 8 | public int stock; 9 | private boolean repeatable; 10 | 11 | public TomeSpell(Recipe recipe) { 12 | stock = 0; 13 | this.recipe = recipe; 14 | repeatable = IntStream.of(recipe.delta) 15 | .anyMatch(x -> x < 0); 16 | } 17 | 18 | @Override 19 | public int getStock() { 20 | return stock; 21 | } 22 | 23 | @Override 24 | public boolean isRepeatable() { 25 | return repeatable; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/AnimationData.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | public class AnimationData { 4 | private static final int SHORT = 100; 5 | private static final int QUICK = 300; 6 | private static final int LONG = 500; 7 | 8 | public static final int STIR_DURATION = LONG; 9 | public static final int RESET_DURATION = QUICK; 10 | public static final int SHELF_TO_POT_DURATION = LONG; 11 | public static final int POT_TO_SHELF_DURATION = SHELF_TO_POT_DURATION; 12 | public static final int SPLASH_DURATION = LONG; 13 | public static final int SHELF_TO_POT_SEPERATION = SHORT; 14 | public static final int POT_TO_SHELF_SEPERATION = SHELF_TO_POT_SEPERATION; 15 | public static final int SHELF_TO_TOME_DURATION = LONG; 16 | public static final int SHELF_TO_TOME_SEPARATION = SHORT; 17 | public static final int SHELF_TO_STOCK_DURATION = QUICK; 18 | public static final int SHELF_TO_STOCK_SEPARATION = SHORT; 19 | public static final int TOME_TO_SHELF_DURATION = LONG; 20 | public static final int TOME_TO_SHELF_SEPARATION = SHORT; 21 | public static final int TOME_TO_LEARNT_DURATION = LONG; 22 | public static final int NEW_SPELL_DURATION = LONG; 23 | public static final int NEW_SPELL_SEPARATION = SHORT; 24 | public static final int POTION_SPAWN_DURATION = LONG; 25 | public static final int POTION_TO_DELIVERY_DURATION = LONG; 26 | public static final int DELIVERY_FADE_DURATION = LONG; 27 | 28 | int start, end; 29 | Integer trigger; 30 | Integer triggerEnd; 31 | 32 | public AnimationData(int start, int duration) { 33 | this.start = start; 34 | this.end = start + duration; 35 | this.trigger = null; 36 | this.triggerEnd = null; 37 | } 38 | 39 | public AnimationData(int start, int duration, Integer triggerAfter, Integer triggerDuration) { 40 | this.start = start; 41 | this.end = start + duration; 42 | this.trigger = triggerAfter == null ? null : start + triggerAfter; 43 | this.triggerEnd = triggerDuration == null ? null : start + triggerAfter + triggerDuration; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/BonusData.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | public class BonusData { 4 | int value; 5 | int amount; 6 | 7 | public BonusData(int amount, int value) { 8 | this.value = value; 9 | this.amount = amount; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/EventData.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | import java.util.List; 4 | 5 | public class EventData { 6 | public static final int LEARN = 0; 7 | public static final int LEARN_PAY = 6; 8 | public static final int NEW_DELIVERIES = 1; 9 | public static final int NEW_TOME_SPELLs = 2; 10 | public static final int PLAYER_SPELL = 3; 11 | public static final int DELIVERY = 4; 12 | public static final int RESET = 5; 13 | 14 | public Integer type, playerIdx, spellId, resultId, tomeIdx, acquired, lost, repeats; 15 | public List spells; 16 | public List animData; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/FrameViewData.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | public class FrameViewData { 7 | public List events; 8 | public List scores; 9 | 10 | public List> playerSpells; 11 | public List tomeSpells, deliveries; 12 | public List inventories; 13 | 14 | public List active; 15 | public Map stock; 16 | public Map bonus; 17 | public Map messages; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/GlobalViewData.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | import java.util.List; 4 | 5 | public class GlobalViewData { 6 | 7 | public List> playerSpells; 8 | public List tomeSpells; 9 | public List deliveries; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/Serializer.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collection; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.stream.Collectors; 8 | import java.util.stream.IntStream; 9 | import java.util.stream.Stream; 10 | 11 | public class Serializer { 12 | static private String nullString = "_"; 13 | static private String mainSeparator = "\n"; 14 | static private String defaultSeparator = " "; 15 | 16 | static private String join(String separator, IntStream stream) { 17 | return stream.mapToObj(String::valueOf).collect(Collectors.joining(separator)); 18 | } 19 | 20 | static private String join(IntStream stream) { 21 | return join(defaultSeparator, stream); 22 | } 23 | 24 | static private String join(String separator, Stream stream) { 25 | return stream.map(obj -> obj == null ? nullString : String.valueOf(obj)).collect(Collectors.joining(separator)); 26 | } 27 | 28 | static private String join(String separator, Object... args) { 29 | return join(separator, Stream.of(args)); 30 | } 31 | 32 | static private String join(Object... args) { 33 | return join(defaultSeparator, args); 34 | } 35 | 36 | static private String join(Collection collection) { 37 | return join(defaultSeparator, collection.stream()); 38 | } 39 | 40 | static private String joinMap(String separator, Map map) { 41 | return map.entrySet().stream() 42 | .map(entry -> join(entry.getKey(), entry.getValue())) 43 | .collect(Collectors.joining(separator)); 44 | } 45 | 46 | static private String joinMap(Map map) { 47 | return joinMap(defaultSeparator, map); 48 | } 49 | 50 | static private String animsSerialize(List anims) { 51 | if (anims == null || anims.size() <= 0) { 52 | return "0"; 53 | } 54 | 55 | return join( 56 | anims.size(), 57 | anims.stream() 58 | .map( 59 | anim -> join( 60 | anim.start, 61 | anim.end, 62 | anim.trigger, 63 | anim.triggerEnd 64 | ) 65 | ) 66 | .collect(Collectors.joining(defaultSeparator)) 67 | ); 68 | } 69 | 70 | static private String spellsSerialize(List spells) { 71 | if (spells == null || spells.size() <= 0) { 72 | return "0"; 73 | } 74 | 75 | return join( 76 | spells.size(), 77 | spells.stream() 78 | .map( 79 | spell -> join( 80 | spell.id, 81 | join(Arrays.stream(spell.delta)), 82 | spell.repeatable, 83 | spell.score 84 | ) 85 | ) 86 | .collect(Collectors.joining(defaultSeparator)) 87 | ); 88 | } 89 | 90 | static private String spellsListsSerialize(List> spellsLists) { 91 | if (spellsLists == null || spellsLists.size() <= 0) { 92 | return "0"; 93 | } 94 | 95 | return join( 96 | mainSeparator, 97 | spellsLists.size(), 98 | spellsLists.stream() 99 | .map(spells -> spellsSerialize(spells)) 100 | .collect(Collectors.joining(mainSeparator)) 101 | ); 102 | } 103 | 104 | static private String eventsSerialize(List events) { 105 | if (events == null || events.size() == 0) { 106 | return "0"; 107 | } 108 | 109 | return join( 110 | mainSeparator, 111 | events.size(), 112 | events.stream() 113 | .map( 114 | event -> join( 115 | mainSeparator, 116 | join( 117 | event.type, 118 | event.playerIdx, 119 | event.spellId, 120 | event.resultId, 121 | event.tomeIdx, 122 | event.acquired, 123 | event.lost 124 | ), 125 | spellsSerialize(event.spells), 126 | animsSerialize(event.animData) 127 | ) 128 | ) 129 | .collect(Collectors.joining(mainSeparator)) 130 | ); 131 | } 132 | 133 | static private String playerSpellsSerialize(List> playerSpells) { 134 | if (playerSpells == null || playerSpells.size() == 0) { 135 | return "0"; 136 | } 137 | 138 | return join( 139 | mainSeparator, 140 | playerSpells.size(), 141 | playerSpells.stream() 142 | .map(spellsIds -> join(spellsIds)) 143 | .collect(Collectors.joining(mainSeparator)) 144 | ); 145 | } 146 | 147 | static private String inventoriesSerialize(List inventories) { 148 | if (inventories == null || inventories.size() == 0) { 149 | return "0"; 150 | } 151 | 152 | return join( 153 | mainSeparator, 154 | inventories.size(), 155 | inventories.stream() 156 | .map(inventory -> join(Arrays.stream(inventory))) 157 | .collect(Collectors.joining(mainSeparator)) 158 | ); 159 | } 160 | 161 | private static Object bonusSerialize(Map bonus) { 162 | return bonus.entrySet().stream() 163 | .map( 164 | entry -> join( 165 | defaultSeparator, 166 | entry.getKey(), 167 | join(",", entry.getValue().value, entry.getValue().amount) 168 | ) 169 | ) 170 | .collect(Collectors.joining(defaultSeparator)); 171 | } 172 | 173 | static public String serialize(FrameViewData frameViewData) { 174 | return join( 175 | mainSeparator, 176 | eventsSerialize(frameViewData.events), 177 | join(frameViewData.scores), 178 | playerSpellsSerialize(frameViewData.playerSpells), 179 | join(frameViewData.tomeSpells), 180 | join(frameViewData.deliveries), 181 | inventoriesSerialize(frameViewData.inventories), 182 | join(frameViewData.active), 183 | joinMap(frameViewData.stock), 184 | bonusSerialize(frameViewData.bonus), 185 | joinMap(mainSeparator, frameViewData.messages) 186 | ); 187 | } 188 | 189 | static public String serialize(GlobalViewData globalViewData) { 190 | return join( 191 | mainSeparator, 192 | spellsListsSerialize(globalViewData.playerSpells), 193 | spellsSerialize(globalViewData.tomeSpells), 194 | spellsSerialize(globalViewData.deliveries) 195 | ); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/SpellData.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | public class SpellData { 4 | public int id; 5 | public int[] delta; 6 | public Boolean repeatable; 7 | public Integer score; 8 | 9 | private SpellData(int id, int[] delta, Boolean repeatable, Integer score) { 10 | this.id = id; 11 | this.delta = delta; 12 | this.repeatable = repeatable; 13 | this.score = score; 14 | } 15 | 16 | public SpellData(int id, int[] delta, boolean repeatable) { 17 | this(id, delta, repeatable, null); 18 | } 19 | 20 | public SpellData(int id, int[] delta, int score) { 21 | this(id, delta, null, score); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/codingame/view/ViewModule.java: -------------------------------------------------------------------------------- 1 | package com.codingame.view; 2 | 3 | import com.codingame.game.Referee; 4 | import com.codingame.gameengine.core.AbstractPlayer; 5 | import com.codingame.gameengine.core.GameManager; 6 | import com.codingame.gameengine.core.Module; 7 | import com.google.inject.Inject; 8 | 9 | public class ViewModule implements Module { 10 | 11 | private GameManager gameManager; 12 | private Referee referee; 13 | 14 | @Inject 15 | ViewModule(GameManager gameManager) { 16 | this.gameManager = gameManager; 17 | gameManager.registerModule(this); 18 | } 19 | 20 | public void setReferee(Referee referee) { 21 | this.referee = referee; 22 | } 23 | 24 | @Override 25 | public final void onGameInit() { 26 | sendGlobalData(); 27 | sendFrameData(); 28 | } 29 | 30 | private void sendFrameData() { 31 | FrameViewData data = referee.getCurrentFrameData(); 32 | gameManager.setViewData("graphics", Serializer.serialize(data)); 33 | } 34 | 35 | private void sendGlobalData() { 36 | GlobalViewData data = referee.getGlobalData(); 37 | gameManager.setViewGlobalData("graphics", Serializer.serialize(data)); 38 | 39 | } 40 | 41 | @Override 42 | public final void onAfterGameTurn() { 43 | sendFrameData(); 44 | } 45 | 46 | @Override 47 | public final void onAfterOnEnd() { 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/resources/view/assets/Background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/Background.jpg -------------------------------------------------------------------------------- /src/main/resources/view/assets/cauldrons_front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/cauldrons_front.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/dial0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/dial0.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/goop.json: -------------------------------------------------------------------------------- 1 | {"frames":{"ANIM_BUBLES0003":{"frame":{"x":0,"y":266,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0004":{"frame":{"x":462,"y":805,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0005":{"frame":{"x":0,"y":707,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0006":{"frame":{"x":231,"y":511,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0007":{"frame":{"x":231,"y":707,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0008":{"frame":{"x":231,"y":462,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0009":{"frame":{"x":0,"y":805,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0010":{"frame":{"x":0,"y":560,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0011":{"frame":{"x":462,"y":98,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0012":{"frame":{"x":0,"y":658,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0013":{"frame":{"x":462,"y":707,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0014":{"frame":{"x":0,"y":854,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0015":{"frame":{"x":462,"y":854,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0016":{"frame":{"x":462,"y":903,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0017":{"frame":{"x":0,"y":511,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0018":{"frame":{"x":0,"y":364,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0019":{"frame":{"x":231,"y":609,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0020":{"frame":{"x":231,"y":133,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0021":{"frame":{"x":0,"y":413,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0022":{"frame":{"x":462,"y":581,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0023":{"frame":{"x":231,"y":854,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0024":{"frame":{"x":0,"y":756,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0025":{"frame":{"x":462,"y":658,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0026":{"frame":{"x":0,"y":609,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0027":{"frame":{"x":462,"y":532,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0028":{"frame":{"x":462,"y":49,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0029":{"frame":{"x":0,"y":462,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0030":{"frame":{"x":231,"y":903,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0031":{"frame":{"x":462,"y":756,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0032":{"frame":{"x":231,"y":560,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0033":{"frame":{"x":462,"y":147,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0034":{"frame":{"x":0,"y":217,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0035":{"frame":{"x":231,"y":0,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0036":{"frame":{"x":231,"y":756,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0037":{"frame":{"x":0,"y":903,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0038":{"frame":{"x":0,"y":84,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0039":{"frame":{"x":0,"y":315,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0040":{"frame":{"x":231,"y":805,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0041":{"frame":{"x":462,"y":0,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0042":{"frame":{"x":231,"y":266,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0043":{"frame":{"x":231,"y":658,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_BUBLES0044":{"frame":{"x":231,"y":399,"w":231,"h":49},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":49},"sourceSize":{"w":231,"h":49}},"ANIM_PLOUF0001":{"frame":{"x":231,"y":49,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0002":{"frame":{"x":329,"y":182,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0003":{"frame":{"x":511,"y":280,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0004":{"frame":{"x":49,"y":0,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0005":{"frame":{"x":609,"y":448,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0006":{"frame":{"x":462,"y":364,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0007":{"frame":{"x":462,"y":280,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0008":{"frame":{"x":693,"y":168,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0009":{"frame":{"x":147,"y":133,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0010":{"frame":{"x":98,"y":133,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0011":{"frame":{"x":693,"y":252,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0012":{"frame":{"x":231,"y":315,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0013":{"frame":{"x":231,"y":182,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0014":{"frame":{"x":609,"y":280,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0015":{"frame":{"x":511,"y":196,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0016":{"frame":{"x":693,"y":588,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0017":{"frame":{"x":280,"y":315,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0018":{"frame":{"x":280,"y":49,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0019":{"frame":{"x":560,"y":196,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0020":{"frame":{"x":378,"y":49,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0021":{"frame":{"x":280,"y":182,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0022":{"frame":{"x":693,"y":84,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0023":{"frame":{"x":693,"y":672,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0024":{"frame":{"x":511,"y":448,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0025":{"frame":{"x":0,"y":133,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0026":{"frame":{"x":693,"y":420,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0027":{"frame":{"x":693,"y":504,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0028":{"frame":{"x":462,"y":196,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0029":{"frame":{"x":560,"y":280,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0030":{"frame":{"x":329,"y":49,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0031":{"frame":{"x":49,"y":133,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0032":{"frame":{"x":329,"y":315,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0033":{"frame":{"x":609,"y":196,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0034":{"frame":{"x":378,"y":182,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0035":{"frame":{"x":511,"y":364,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0036":{"frame":{"x":609,"y":364,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0037":{"frame":{"x":462,"y":448,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0038":{"frame":{"x":693,"y":756,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0039":{"frame":{"x":693,"y":336,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0040":{"frame":{"x":560,"y":448,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0041":{"frame":{"x":0,"y":0,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0042":{"frame":{"x":98,"y":0,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0043":{"frame":{"x":560,"y":364,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0044":{"frame":{"x":693,"y":0,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}},"ANIM_PLOUF0045":{"frame":{"x":378,"y":315,"w":49,"h":84},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":49,"h":84},"sourceSize":{"w":49,"h":84}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"goop.png","size":{"w":742,"h":952},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/goop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/goop.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/potioff.json: -------------------------------------------------------------------------------- 1 | {"frames":{"ANIM_DISP_BOUTEILLE_10001":{"frame":{"x":0,"y":0,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10003":{"frame":{"x":0,"y":288,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10005":{"frame":{"x":293,"y":0,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10007":{"frame":{"x":293,"y":288,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10009":{"frame":{"x":0,"y":576,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10011":{"frame":{"x":293,"y":576,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10013":{"frame":{"x":586,"y":0,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10015":{"frame":{"x":586,"y":288,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10017":{"frame":{"x":586,"y":576,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10019":{"frame":{"x":0,"y":864,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10021":{"frame":{"x":293,"y":864,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10023":{"frame":{"x":586,"y":864,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10025":{"frame":{"x":879,"y":0,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10027":{"frame":{"x":879,"y":288,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10029":{"frame":{"x":879,"y":576,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10031":{"frame":{"x":879,"y":864,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10033":{"frame":{"x":0,"y":1152,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10035":{"frame":{"x":293,"y":1152,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10037":{"frame":{"x":586,"y":1152,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10039":{"frame":{"x":879,"y":1152,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10040":{"frame":{"x":1172,"y":0,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10042":{"frame":{"x":1172,"y":288,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10044":{"frame":{"x":1172,"y":576,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}},"ANIM_DISP_BOUTEILLE_10046":{"frame":{"x":1172,"y":864,"w":293,"h":288},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":293,"h":288},"sourceSize":{"w":293,"h":288}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"potioff.png","size":{"w":1465,"h":1440},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/potioff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/potioff.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/potion.json: -------------------------------------------------------------------------------- 1 | {"frames":{"APP_BOUTEILLE_10001":{"frame":{"x":0,"y":0,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10003":{"frame":{"x":349,"y":0,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10005":{"frame":{"x":0,"y":367,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10007":{"frame":{"x":349,"y":367,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10009":{"frame":{"x":698,"y":0,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10011":{"frame":{"x":698,"y":367,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10013":{"frame":{"x":0,"y":734,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10015":{"frame":{"x":349,"y":734,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10017":{"frame":{"x":698,"y":734,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10019":{"frame":{"x":1047,"y":0,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10021":{"frame":{"x":1047,"y":367,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10023":{"frame":{"x":1047,"y":734,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10025":{"frame":{"x":0,"y":1101,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10027":{"frame":{"x":349,"y":1101,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10029":{"frame":{"x":698,"y":1101,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10031":{"frame":{"x":1047,"y":1101,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10033":{"frame":{"x":1396,"y":0,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10035":{"frame":{"x":1396,"y":367,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10037":{"frame":{"x":1396,"y":734,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}},"APP_BOUTEILLE_10039":{"frame":{"x":1396,"y":1101,"w":349,"h":367},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":349,"h":367},"sourceSize":{"w":349,"h":367}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"potion.png","size":{"w":1745,"h":1468},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/potion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/potion.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/read_blink_0.json: -------------------------------------------------------------------------------- 1 | {"frames":{"ANIM_LECTURE0001":{"frame":{"x":0,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0003":{"frame":{"x":924,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0005":{"frame":{"x":231,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0007":{"frame":{"x":1386,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0009":{"frame":{"x":1386,"y":1050,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0011":{"frame":{"x":462,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0013":{"frame":{"x":924,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0015":{"frame":{"x":924,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0017":{"frame":{"x":924,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0019":{"frame":{"x":924,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0021":{"frame":{"x":1155,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0023":{"frame":{"x":462,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0025":{"frame":{"x":1155,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0027":{"frame":{"x":231,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0029":{"frame":{"x":693,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0031":{"frame":{"x":924,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0033":{"frame":{"x":693,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0035":{"frame":{"x":0,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0037":{"frame":{"x":1155,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0039":{"frame":{"x":0,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0041":{"frame":{"x":693,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0043":{"frame":{"x":462,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0045":{"frame":{"x":231,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0047":{"frame":{"x":1155,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0049":{"frame":{"x":231,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0051":{"frame":{"x":1155,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0053":{"frame":{"x":1155,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0055":{"frame":{"x":693,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0057":{"frame":{"x":0,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0059":{"frame":{"x":462,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0061":{"frame":{"x":0,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0063":{"frame":{"x":231,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0065":{"frame":{"x":462,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0067":{"frame":{"x":231,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0069":{"frame":{"x":693,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0071":{"frame":{"x":462,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0073":{"frame":{"x":0,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE0075":{"frame":{"x":693,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIME_EYES0001":{"frame":{"x":1386,"y":350,"w":245,"h":350},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":245,"h":350},"sourceSize":{"w":245,"h":350}},"ANIME_EYES0002":{"frame":{"x":1386,"y":0,"w":245,"h":350},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":245,"h":350},"sourceSize":{"w":245,"h":350}},"ANIME_EYES0003":{"frame":{"x":1386,"y":700,"w":245,"h":350},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":245,"h":350},"sourceSize":{"w":245,"h":350}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"read_blink_0.png","size":{"w":1631,"h":1746},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/read_blink_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/read_blink_0.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/read_blink_1.json: -------------------------------------------------------------------------------- 1 | {"frames":{"ANIM_LECTURE_R0001":{"frame":{"x":693,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0003":{"frame":{"x":1400,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0005":{"frame":{"x":1386,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0007":{"frame":{"x":0,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0009":{"frame":{"x":0,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0011":{"frame":{"x":231,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0013":{"frame":{"x":462,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0015":{"frame":{"x":231,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0017":{"frame":{"x":0,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0019":{"frame":{"x":1155,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0021":{"frame":{"x":231,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0023":{"frame":{"x":1400,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0025":{"frame":{"x":693,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0027":{"frame":{"x":924,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0029":{"frame":{"x":693,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0031":{"frame":{"x":0,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0033":{"frame":{"x":693,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0035":{"frame":{"x":1400,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0037":{"frame":{"x":924,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0039":{"frame":{"x":462,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0041":{"frame":{"x":1155,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0043":{"frame":{"x":462,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0045":{"frame":{"x":924,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0047":{"frame":{"x":462,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0049":{"frame":{"x":0,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0051":{"frame":{"x":231,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0053":{"frame":{"x":462,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0055":{"frame":{"x":924,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0057":{"frame":{"x":693,"y":0,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0059":{"frame":{"x":924,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0061":{"frame":{"x":231,"y":1455,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0063":{"frame":{"x":924,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0065":{"frame":{"x":693,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0067":{"frame":{"x":462,"y":582,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0069":{"frame":{"x":231,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0071":{"frame":{"x":1400,"y":291,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0073":{"frame":{"x":1400,"y":873,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIM_LECTURE_R0075":{"frame":{"x":0,"y":1164,"w":231,"h":291},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":231,"h":291},"sourceSize":{"w":231,"h":291}},"ANIME_EYES_R0001":{"frame":{"x":1155,"y":0,"w":245,"h":350},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":245,"h":350},"sourceSize":{"w":245,"h":350}},"ANIME_EYES_R0002":{"frame":{"x":1155,"y":350,"w":245,"h":350},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":245,"h":350},"sourceSize":{"w":245,"h":350}},"ANIME_EYES_R0003":{"frame":{"x":1155,"y":700,"w":245,"h":350},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":245,"h":350},"sourceSize":{"w":245,"h":350}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"read_blink_1.png","size":{"w":1631,"h":1746},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/read_blink_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/read_blink_1.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/rest_0.json: -------------------------------------------------------------------------------- 1 | {"frames":{"ANIM_CANALYSE0001":{"frame":{"x":0,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0003":{"frame":{"x":266,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0005":{"frame":{"x":532,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0007":{"frame":{"x":0,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0009":{"frame":{"x":266,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0011":{"frame":{"x":532,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0013":{"frame":{"x":798,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0015":{"frame":{"x":798,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0017":{"frame":{"x":0,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0019":{"frame":{"x":266,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0021":{"frame":{"x":532,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0023":{"frame":{"x":798,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0025":{"frame":{"x":1064,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0027":{"frame":{"x":1064,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0029":{"frame":{"x":1064,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0031":{"frame":{"x":1330,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0033":{"frame":{"x":1330,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0035":{"frame":{"x":1330,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0037":{"frame":{"x":0,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0039":{"frame":{"x":266,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0041":{"frame":{"x":532,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0043":{"frame":{"x":798,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0045":{"frame":{"x":1064,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0047":{"frame":{"x":1330,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE0049":{"frame":{"x":1596,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"rest_0.png","size":{"w":1862,"h":1696},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/rest_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/rest_0.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/rest_1.json: -------------------------------------------------------------------------------- 1 | {"frames":{"ANIM_CANALYSE_R0001":{"frame":{"x":0,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0003":{"frame":{"x":266,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0005":{"frame":{"x":532,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0007":{"frame":{"x":0,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0009":{"frame":{"x":266,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0011":{"frame":{"x":532,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0013":{"frame":{"x":798,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0015":{"frame":{"x":798,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0017":{"frame":{"x":0,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0019":{"frame":{"x":266,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0021":{"frame":{"x":532,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0023":{"frame":{"x":798,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0025":{"frame":{"x":1064,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0027":{"frame":{"x":1064,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0029":{"frame":{"x":1064,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0031":{"frame":{"x":1330,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0033":{"frame":{"x":1330,"y":424,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0035":{"frame":{"x":1330,"y":848,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0037":{"frame":{"x":0,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0039":{"frame":{"x":266,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0041":{"frame":{"x":532,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0043":{"frame":{"x":798,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0045":{"frame":{"x":1064,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0047":{"frame":{"x":1330,"y":1272,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}},"ANIM_CANALYSE_R0049":{"frame":{"x":1596,"y":0,"w":266,"h":424},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":266,"h":424},"sourceSize":{"w":266,"h":424}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"rest_1.png","size":{"w":1862,"h":1696},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/rest_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/rest_1.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/rupee.json: -------------------------------------------------------------------------------- 1 | {"frames":{"ANIM_Diams0001":{"frame":{"x":0,"y":0,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0003":{"frame":{"x":0,"y":94,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0005":{"frame":{"x":104,"y":0,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0007":{"frame":{"x":104,"y":94,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0009":{"frame":{"x":0,"y":188,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0011":{"frame":{"x":104,"y":188,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0013":{"frame":{"x":208,"y":0,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0015":{"frame":{"x":208,"y":94,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0017":{"frame":{"x":208,"y":188,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0019":{"frame":{"x":0,"y":282,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0021":{"frame":{"x":104,"y":282,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0023":{"frame":{"x":208,"y":282,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"ANIM_Diams0025":{"frame":{"x":312,"y":0,"w":104,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":104,"h":94},"sourceSize":{"w":104,"h":94}},"diams":{"frame":{"x":312,"y":94,"w":78,"h":73},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":78,"h":73},"sourceSize":{"w":78,"h":73}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"rupee.png","size":{"w":416,"h":376},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/rupee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/rupee.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/sprites.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "Cadre": { 4 | "frame": { 5 | "x": 464, 6 | "y": 769, 7 | "w": 241, 8 | "h": 74 9 | }, 10 | "rotated": false, 11 | "trimmed": false, 12 | "spriteSourceSize": { 13 | "x": 0, 14 | "y": 0, 15 | "w": 241, 16 | "h": 74 17 | }, 18 | "sourceSize": { 19 | "w": 241, 20 | "h": 74 21 | } 22 | }, 23 | "Forme_Commandes": { 24 | "frame": { 25 | "x": 0, 26 | "y": 769, 27 | "w": 318, 28 | "h": 128 29 | }, 30 | "rotated": false, 31 | "trimmed": false, 32 | "spriteSourceSize": { 33 | "x": 0, 34 | "y": 0, 35 | "w": 318, 36 | "h": 128 37 | }, 38 | "sourceSize": { 39 | "w": 318, 40 | "h": 128 41 | } 42 | }, 43 | "Forme_Recettes_Apprises": { 44 | "frame": { 45 | "x": 289, 46 | "y": 898, 47 | "w": 229, 48 | "h": 62 49 | }, 50 | "rotated": false, 51 | "trimmed": false, 52 | "spriteSourceSize": { 53 | "x": 0, 54 | "y": 0, 55 | "w": 229, 56 | "h": 62 57 | }, 58 | "sourceSize": { 59 | "w": 229, 60 | "h": 62 61 | } 62 | }, 63 | "Forme_Recettes_Dispo": { 64 | "frame": { 65 | "x": 0, 66 | "y": 898, 67 | "w": 288, 68 | "h": 72 69 | }, 70 | "rotated": false, 71 | "trimmed": false, 72 | "spriteSourceSize": { 73 | "x": 0, 74 | "y": 0, 75 | "w": 288, 76 | "h": 72 77 | }, 78 | "sourceSize": { 79 | "w": 288, 80 | "h": 72 81 | } 82 | }, 83 | "HUD_0": { 84 | "frame": { 85 | "x": 0, 86 | "y": 325, 87 | "w": 709, 88 | "h": 223 89 | }, 90 | "rotated": false, 91 | "trimmed": false, 92 | "spriteSourceSize": { 93 | "x": 0, 94 | "y": 0, 95 | "w": 709, 96 | "h": 223 97 | }, 98 | "sourceSize": { 99 | "w": 709, 100 | "h": 223 101 | } 102 | }, 103 | "HUD_1": { 104 | "frame": { 105 | "x": 0, 106 | "y": 549, 107 | "w": 691, 108 | "h": 219 109 | }, 110 | "rotated": false, 111 | "trimmed": false, 112 | "spriteSourceSize": { 113 | "x": 0, 114 | "y": 0, 115 | "w": 691, 116 | "h": 219 117 | }, 118 | "sourceSize": { 119 | "w": 691, 120 | "h": 219 121 | } 122 | }, 123 | "Item-1": { 124 | "frame": { 125 | "x": 391, 126 | "y": 769, 127 | "w": 72, 128 | "h": 75 129 | }, 130 | "rotated": false, 131 | "trimmed": false, 132 | "spriteSourceSize": { 133 | "x": 0, 134 | "y": 0, 135 | "w": 72, 136 | "h": 75 137 | }, 138 | "sourceSize": { 139 | "w": 72, 140 | "h": 75 141 | } 142 | }, 143 | "Item-2": { 144 | "frame": { 145 | "x": 692, 146 | "y": 549, 147 | "w": 74, 148 | "h": 73 149 | }, 150 | "rotated": false, 151 | "trimmed": false, 152 | "spriteSourceSize": { 153 | "x": 0, 154 | "y": 0, 155 | "w": 74, 156 | "h": 73 157 | }, 158 | "sourceSize": { 159 | "w": 74, 160 | "h": 73 161 | } 162 | }, 163 | "Item-3": { 164 | "frame": { 165 | "x": 319, 166 | "y": 769, 167 | "w": 71, 168 | "h": 78 169 | }, 170 | "rotated": false, 171 | "trimmed": false, 172 | "spriteSourceSize": { 173 | "x": 0, 174 | "y": 0, 175 | "w": 71, 176 | "h": 78 177 | }, 178 | "sourceSize": { 179 | "w": 71, 180 | "h": 78 181 | } 182 | }, 183 | "Item-4": { 184 | "frame": { 185 | "x": 706, 186 | "y": 769, 187 | "w": 70, 188 | "h": 74 189 | }, 190 | "rotated": false, 191 | "trimmed": false, 192 | "spriteSourceSize": { 193 | "x": 0, 194 | "y": 0, 195 | "w": 70, 196 | "h": 74 197 | }, 198 | "sourceSize": { 199 | "w": 70, 200 | "h": 74 201 | } 202 | }, 203 | "logo.png": { 204 | "frame": { 205 | "x": 0, 206 | "y": 0, 207 | "w": 800, 208 | "h": 324 209 | }, 210 | "rotated": false, 211 | "trimmed": false, 212 | "spriteSourceSize": { 213 | "x": 0, 214 | "y": 0, 215 | "w": 800, 216 | "h": 324 217 | }, 218 | "sourceSize": { 219 | "w": 800, 220 | "h": 324 221 | } 222 | } 223 | }, 224 | "meta": { 225 | "app": "https://www.leshylabs.com/apps/sstool/", 226 | "version": "Leshy SpriteSheet Tool v0.8.4", 227 | "image": "spritesheet.png", 228 | "size": { 229 | "w": 800, 230 | "h": 970 231 | }, 232 | "scale": 1 233 | } 234 | } -------------------------------------------------------------------------------- /src/main/resources/view/assets/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/spritesheet.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/stir_0.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": { 3 | "ANIM_TOUILLE0001": { 4 | "frame": { 5 | "x": 0, 6 | "y": 0, 7 | "w": 329, 8 | "h": 336 9 | }, 10 | "rotated": false, 11 | "trimmed": false, 12 | "spriteSourceSize": { 13 | "x": 0, 14 | "y": 0, 15 | "w": 329, 16 | "h": 336 17 | }, 18 | "sourceSize": { 19 | "w": 329, 20 | "h": 336 21 | } 22 | }, 23 | "ANIM_TOUILLE0003": { 24 | "frame": { 25 | "x": 329, 26 | "y": 0, 27 | "w": 329, 28 | "h": 336 29 | }, 30 | "rotated": false, 31 | "trimmed": false, 32 | "spriteSourceSize": { 33 | "x": 0, 34 | "y": 0, 35 | "w": 329, 36 | "h": 336 37 | }, 38 | "sourceSize": { 39 | "w": 329, 40 | "h": 336 41 | } 42 | }, 43 | "ANIM_TOUILLE0005": { 44 | "frame": { 45 | "x": 0, 46 | "y": 336, 47 | "w": 329, 48 | "h": 336 49 | }, 50 | "rotated": false, 51 | "trimmed": false, 52 | "spriteSourceSize": { 53 | "x": 0, 54 | "y": 0, 55 | "w": 329, 56 | "h": 336 57 | }, 58 | "sourceSize": { 59 | "w": 329, 60 | "h": 336 61 | } 62 | }, 63 | "ANIM_TOUILLE0007": { 64 | "frame": { 65 | "x": 329, 66 | "y": 336, 67 | "w": 329, 68 | "h": 336 69 | }, 70 | "rotated": false, 71 | "trimmed": false, 72 | "spriteSourceSize": { 73 | "x": 0, 74 | "y": 0, 75 | "w": 329, 76 | "h": 336 77 | }, 78 | "sourceSize": { 79 | "w": 329, 80 | "h": 336 81 | } 82 | }, 83 | "ANIM_TOUILLE0009": { 84 | "frame": { 85 | "x": 658, 86 | "y": 0, 87 | "w": 329, 88 | "h": 336 89 | }, 90 | "rotated": false, 91 | "trimmed": false, 92 | "spriteSourceSize": { 93 | "x": 0, 94 | "y": 0, 95 | "w": 329, 96 | "h": 336 97 | }, 98 | "sourceSize": { 99 | "w": 329, 100 | "h": 336 101 | } 102 | }, 103 | "ANIM_TOUILLE0011": { 104 | "frame": { 105 | "x": 658, 106 | "y": 336, 107 | "w": 329, 108 | "h": 336 109 | }, 110 | "rotated": false, 111 | "trimmed": false, 112 | "spriteSourceSize": { 113 | "x": 0, 114 | "y": 0, 115 | "w": 329, 116 | "h": 336 117 | }, 118 | "sourceSize": { 119 | "w": 329, 120 | "h": 336 121 | } 122 | }, 123 | "ANIM_TOUILLE0013": { 124 | "frame": { 125 | "x": 0, 126 | "y": 672, 127 | "w": 329, 128 | "h": 336 129 | }, 130 | "rotated": false, 131 | "trimmed": false, 132 | "spriteSourceSize": { 133 | "x": 0, 134 | "y": 0, 135 | "w": 329, 136 | "h": 336 137 | }, 138 | "sourceSize": { 139 | "w": 329, 140 | "h": 336 141 | } 142 | }, 143 | "ANIM_TOUILLE0015": { 144 | "frame": { 145 | "x": 329, 146 | "y": 672, 147 | "w": 329, 148 | "h": 336 149 | }, 150 | "rotated": false, 151 | "trimmed": false, 152 | "spriteSourceSize": { 153 | "x": 0, 154 | "y": 0, 155 | "w": 329, 156 | "h": 336 157 | }, 158 | "sourceSize": { 159 | "w": 329, 160 | "h": 336 161 | } 162 | }, 163 | "ANIM_TOUILLE0017": { 164 | "frame": { 165 | "x": 658, 166 | "y": 672, 167 | "w": 329, 168 | "h": 336 169 | }, 170 | "rotated": false, 171 | "trimmed": false, 172 | "spriteSourceSize": { 173 | "x": 0, 174 | "y": 0, 175 | "w": 329, 176 | "h": 336 177 | }, 178 | "sourceSize": { 179 | "w": 329, 180 | "h": 336 181 | } 182 | }, 183 | "ANIM_TOUILLE0019": { 184 | "frame": { 185 | "x": 987, 186 | "y": 0, 187 | "w": 329, 188 | "h": 336 189 | }, 190 | "rotated": false, 191 | "trimmed": false, 192 | "spriteSourceSize": { 193 | "x": 0, 194 | "y": 0, 195 | "w": 329, 196 | "h": 336 197 | }, 198 | "sourceSize": { 199 | "w": 329, 200 | "h": 336 201 | } 202 | }, 203 | "ANIM_TOUILLE0021": { 204 | "frame": { 205 | "x": 987, 206 | "y": 336, 207 | "w": 329, 208 | "h": 336 209 | }, 210 | "rotated": false, 211 | "trimmed": false, 212 | "spriteSourceSize": { 213 | "x": 0, 214 | "y": 0, 215 | "w": 329, 216 | "h": 336 217 | }, 218 | "sourceSize": { 219 | "w": 329, 220 | "h": 336 221 | } 222 | }, 223 | "ANIM_TOUILLE0023": { 224 | "frame": { 225 | "x": 987, 226 | "y": 672, 227 | "w": 329, 228 | "h": 336 229 | }, 230 | "rotated": false, 231 | "trimmed": false, 232 | "spriteSourceSize": { 233 | "x": 0, 234 | "y": 0, 235 | "w": 329, 236 | "h": 336 237 | }, 238 | "sourceSize": { 239 | "w": 329, 240 | "h": 336 241 | } 242 | }, 243 | "ANIM_TOUILLE0025": { 244 | "frame": { 245 | "x": 0, 246 | "y": 1008, 247 | "w": 329, 248 | "h": 336 249 | }, 250 | "rotated": false, 251 | "trimmed": false, 252 | "spriteSourceSize": { 253 | "x": 0, 254 | "y": 0, 255 | "w": 329, 256 | "h": 336 257 | }, 258 | "sourceSize": { 259 | "w": 329, 260 | "h": 336 261 | } 262 | }, 263 | "ANIM_TOUILLE0027": { 264 | "frame": { 265 | "x": 329, 266 | "y": 1008, 267 | "w": 329, 268 | "h": 336 269 | }, 270 | "rotated": false, 271 | "trimmed": false, 272 | "spriteSourceSize": { 273 | "x": 0, 274 | "y": 0, 275 | "w": 329, 276 | "h": 336 277 | }, 278 | "sourceSize": { 279 | "w": 329, 280 | "h": 336 281 | } 282 | }, 283 | "ANIM_TOUILLE0029": { 284 | "frame": { 285 | "x": 658, 286 | "y": 1008, 287 | "w": 329, 288 | "h": 336 289 | }, 290 | "rotated": false, 291 | "trimmed": false, 292 | "spriteSourceSize": { 293 | "x": 0, 294 | "y": 0, 295 | "w": 329, 296 | "h": 336 297 | }, 298 | "sourceSize": { 299 | "w": 329, 300 | "h": 336 301 | } 302 | }, 303 | "ANIM_TOUILLE0031": { 304 | "frame": { 305 | "x": 987, 306 | "y": 1008, 307 | "w": 329, 308 | "h": 336 309 | }, 310 | "rotated": false, 311 | "trimmed": false, 312 | "spriteSourceSize": { 313 | "x": 0, 314 | "y": 0, 315 | "w": 329, 316 | "h": 336 317 | }, 318 | "sourceSize": { 319 | "w": 329, 320 | "h": 336 321 | } 322 | }, 323 | "ANIM_TOUILLE0033": { 324 | "frame": { 325 | "x": 1316, 326 | "y": 0, 327 | "w": 329, 328 | "h": 336 329 | }, 330 | "rotated": false, 331 | "trimmed": false, 332 | "spriteSourceSize": { 333 | "x": 0, 334 | "y": 0, 335 | "w": 329, 336 | "h": 336 337 | }, 338 | "sourceSize": { 339 | "w": 329, 340 | "h": 336 341 | } 342 | }, 343 | "ANIM_TOUILLE0035": { 344 | "frame": { 345 | "x": 1316, 346 | "y": 336, 347 | "w": 329, 348 | "h": 336 349 | }, 350 | "rotated": false, 351 | "trimmed": false, 352 | "spriteSourceSize": { 353 | "x": 0, 354 | "y": 0, 355 | "w": 329, 356 | "h": 336 357 | }, 358 | "sourceSize": { 359 | "w": 329, 360 | "h": 336 361 | } 362 | }, 363 | "ANIM_TOUILLE0037": { 364 | "frame": { 365 | "x": 1316, 366 | "y": 672, 367 | "w": 329, 368 | "h": 336 369 | }, 370 | "rotated": false, 371 | "trimmed": false, 372 | "spriteSourceSize": { 373 | "x": 0, 374 | "y": 0, 375 | "w": 329, 376 | "h": 336 377 | }, 378 | "sourceSize": { 379 | "w": 329, 380 | "h": 336 381 | } 382 | }, 383 | "ANIM_TOUILLE0039": { 384 | "frame": { 385 | "x": 1316, 386 | "y": 1008, 387 | "w": 329, 388 | "h": 336 389 | }, 390 | "rotated": false, 391 | "trimmed": false, 392 | "spriteSourceSize": { 393 | "x": 0, 394 | "y": 0, 395 | "w": 329, 396 | "h": 336 397 | }, 398 | "sourceSize": { 399 | "w": 329, 400 | "h": 336 401 | } 402 | }, 403 | "ANIM_TOUILLE0041": { 404 | "frame": { 405 | "x": 0, 406 | "y": 1344, 407 | "w": 329, 408 | "h": 336 409 | }, 410 | "rotated": false, 411 | "trimmed": false, 412 | "spriteSourceSize": { 413 | "x": 0, 414 | "y": 0, 415 | "w": 329, 416 | "h": 336 417 | }, 418 | "sourceSize": { 419 | "w": 329, 420 | "h": 336 421 | } 422 | }, 423 | "ANIM_TOUILLE0043": { 424 | "frame": { 425 | "x": 329, 426 | "y": 1344, 427 | "w": 329, 428 | "h": 336 429 | }, 430 | "rotated": false, 431 | "trimmed": false, 432 | "spriteSourceSize": { 433 | "x": 0, 434 | "y": 0, 435 | "w": 329, 436 | "h": 336 437 | }, 438 | "sourceSize": { 439 | "w": 329, 440 | "h": 336 441 | } 442 | }, 443 | "ANIM_TOUILLE0045": { 444 | "frame": { 445 | "x": 658, 446 | "y": 1344, 447 | "w": 329, 448 | "h": 336 449 | }, 450 | "rotated": false, 451 | "trimmed": false, 452 | "spriteSourceSize": { 453 | "x": 0, 454 | "y": 0, 455 | "w": 329, 456 | "h": 336 457 | }, 458 | "sourceSize": { 459 | "w": 329, 460 | "h": 336 461 | } 462 | }, 463 | "ANIM_TOUILLE0047": { 464 | "frame": { 465 | "x": 987, 466 | "y": 1344, 467 | "w": 329, 468 | "h": 336 469 | }, 470 | "rotated": false, 471 | "trimmed": false, 472 | "spriteSourceSize": { 473 | "x": 0, 474 | "y": 0, 475 | "w": 329, 476 | "h": 336 477 | }, 478 | "sourceSize": { 479 | "w": 329, 480 | "h": 336 481 | } 482 | }, 483 | "ANIM_TOUILLE0049": { 484 | "frame": { 485 | "x": 1316, 486 | "y": 1344, 487 | "w": 329, 488 | "h": 336 489 | }, 490 | "rotated": false, 491 | "trimmed": false, 492 | "spriteSourceSize": { 493 | "x": 0, 494 | "y": 0, 495 | "w": 329, 496 | "h": 336 497 | }, 498 | "sourceSize": { 499 | "w": 329, 500 | "h": 336 501 | } 502 | }, 503 | "ANIM_TOUILLE0051": { 504 | "frame": { 505 | "x": 1645, 506 | "y": 0, 507 | "w": 329, 508 | "h": 336 509 | }, 510 | "rotated": false, 511 | "trimmed": false, 512 | "spriteSourceSize": { 513 | "x": 0, 514 | "y": 0, 515 | "w": 329, 516 | "h": 336 517 | }, 518 | "sourceSize": { 519 | "w": 329, 520 | "h": 336 521 | } 522 | }, 523 | "ANIM_TOUILLE0053": { 524 | "frame": { 525 | "x": 1645, 526 | "y": 336, 527 | "w": 329, 528 | "h": 336 529 | }, 530 | "rotated": false, 531 | "trimmed": false, 532 | "spriteSourceSize": { 533 | "x": 0, 534 | "y": 0, 535 | "w": 329, 536 | "h": 336 537 | }, 538 | "sourceSize": { 539 | "w": 329, 540 | "h": 336 541 | } 542 | }, 543 | "ANIM_TOUILLE0055": { 544 | "frame": { 545 | "x": 1645, 546 | "y": 672, 547 | "w": 329, 548 | "h": 336 549 | }, 550 | "rotated": false, 551 | "trimmed": false, 552 | "spriteSourceSize": { 553 | "x": 0, 554 | "y": 0, 555 | "w": 329, 556 | "h": 336 557 | }, 558 | "sourceSize": { 559 | "w": 329, 560 | "h": 336 561 | } 562 | }, 563 | "ANIM_TOUILLE0057": { 564 | "frame": { 565 | "x": 1645, 566 | "y": 1008, 567 | "w": 329, 568 | "h": 336 569 | }, 570 | "rotated": false, 571 | "trimmed": false, 572 | "spriteSourceSize": { 573 | "x": 0, 574 | "y": 0, 575 | "w": 329, 576 | "h": 336 577 | }, 578 | "sourceSize": { 579 | "w": 329, 580 | "h": 336 581 | } 582 | }, 583 | "ANIM_TOUILLE0059": { 584 | "frame": { 585 | "x": 1645, 586 | "y": 1344, 587 | "w": 329, 588 | "h": 336 589 | }, 590 | "rotated": false, 591 | "trimmed": false, 592 | "spriteSourceSize": { 593 | "x": 0, 594 | "y": 0, 595 | "w": 329, 596 | "h": 336 597 | }, 598 | "sourceSize": { 599 | "w": 329, 600 | "h": 336 601 | } 602 | } 603 | }, 604 | "meta": { 605 | "app": "https://www.leshylabs.com/apps/sstool/", 606 | "version": "Leshy SpriteSheet Tool v0.8.4", 607 | "image": "stir_0.png", 608 | "size": { 609 | "w": 1974, 610 | "h": 1680 611 | }, 612 | "scale": 1 613 | } 614 | } -------------------------------------------------------------------------------- /src/main/resources/view/assets/stir_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/stir_0.png -------------------------------------------------------------------------------- /src/main/resources/view/assets/stir_1.json: -------------------------------------------------------------------------------- 1 | {"frames":{"ANIM_TOUILLE_R0001":{"frame":{"x":0,"y":0,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0003":{"frame":{"x":329,"y":0,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0005":{"frame":{"x":0,"y":336,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0007":{"frame":{"x":329,"y":336,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0009":{"frame":{"x":658,"y":0,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0011":{"frame":{"x":658,"y":336,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0013":{"frame":{"x":0,"y":672,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0015":{"frame":{"x":329,"y":672,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0017":{"frame":{"x":658,"y":672,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0019":{"frame":{"x":987,"y":0,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0021":{"frame":{"x":987,"y":336,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0023":{"frame":{"x":987,"y":672,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0025":{"frame":{"x":0,"y":1008,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0027":{"frame":{"x":329,"y":1008,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0029":{"frame":{"x":658,"y":1008,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0031":{"frame":{"x":987,"y":1008,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0033":{"frame":{"x":1316,"y":0,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0035":{"frame":{"x":1316,"y":336,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0037":{"frame":{"x":1316,"y":672,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0039":{"frame":{"x":1316,"y":1008,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0041":{"frame":{"x":0,"y":1344,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0043":{"frame":{"x":329,"y":1344,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0045":{"frame":{"x":658,"y":1344,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0047":{"frame":{"x":987,"y":1344,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0049":{"frame":{"x":1316,"y":1344,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0051":{"frame":{"x":1645,"y":0,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0053":{"frame":{"x":1645,"y":336,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0055":{"frame":{"x":1645,"y":672,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0057":{"frame":{"x":1645,"y":1008,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}},"ANIM_TOUILLE_R0059":{"frame":{"x":1645,"y":1344,"w":329,"h":336},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":329,"h":336},"sourceSize":{"w":329,"h":336}}},"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"stir_1.png","size":{"w":1974,"h":1680},"scale":1}} -------------------------------------------------------------------------------- /src/main/resources/view/assets/stir_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodinGame/FallChallenge2020/5c0cbf33a0463fd56f09bd5b0c6f1b6be86001eb/src/main/resources/view/assets/stir_1.png -------------------------------------------------------------------------------- /src/main/resources/view/config.js: -------------------------------------------------------------------------------- 1 | import { ViewModule, api } from './graphics/ViewModule.js' 2 | import { EndScreenModule } from './endscreen-module/EndScreenModule.js' 3 | import { TooltipModule } from './tooltip/TooltipModule.js' 4 | 5 | export const modules = [ 6 | ViewModule, 7 | TooltipModule, 8 | EndScreenModule 9 | ] 10 | 11 | export const playerColors = [ 12 | '#22a1e4', // curious blue 13 | '#ff1d5c', // radical red 14 | '#de6ddf', // lavender pink 15 | '#6ac371', // mantis green 16 | '#9975e2', // medium purple 17 | '#3ac5ca', // scooter blue 18 | '#ff0000' // solid red 19 | ] 20 | export const gameName = 'Fall2020' 21 | 22 | export const stepByStepAnimateSpeed = 3 23 | 24 | export const options = [ 25 | { 26 | title: 'HIDE EXHAUSTED SPELLS', 27 | get: function () { 28 | return api.options.hideExhaustedSpells 29 | }, 30 | set: function (value) { 31 | if (api.viewModule && api.options.hideExhaustedSpells !== value) { 32 | api.options.hideExhaustedSpells = value 33 | api.updateSpells(api) 34 | } 35 | }, 36 | values: { 37 | ON: true, 38 | OFF: false 39 | } 40 | } 41 | ] 42 | -------------------------------------------------------------------------------- /src/main/resources/view/graphics/Deserializer.js: -------------------------------------------------------------------------------- 1 | const MAIN_SEPARATOR = '\n' 2 | const DEFAULT_SEPARATOR = ' ' 3 | 4 | function parseEvent (raw) { 5 | const input = raw.split(DEFAULT_SEPARATOR) 6 | let idx = 0 7 | 8 | return { 9 | type: +input[idx++], 10 | playerIdx: +input[idx++], 11 | spellId: +input[idx++], 12 | resultId: +input[idx++], 13 | tomeIdx: +input[idx++], 14 | acquired: +input[idx++], 15 | lost: +input[idx++] 16 | } 17 | } 18 | 19 | function parseSpells (raw) { 20 | const input = raw.split(DEFAULT_SEPARATOR) 21 | let idx = 0 22 | const spells = [] 23 | const toIdx = parseInt(input[idx]) * 7 + ++idx 24 | 25 | while (idx < toIdx) { 26 | spells.push({ 27 | id: +input[idx++], 28 | delta: [+input[idx++], +input[idx++], +input[idx++], +input[idx++]], 29 | repeatable: input[idx++] === 'true', 30 | score: +input[idx++] 31 | }) 32 | } 33 | 34 | return spells 35 | } 36 | 37 | function parseAnimData (raw) { 38 | const input = raw.split(DEFAULT_SEPARATOR) 39 | let idx = 0 40 | const animData = [] 41 | const toIdx = parseInt(input[idx]) * 4 + ++idx 42 | 43 | while (idx < toIdx) { 44 | animData.push({ 45 | start: +input[idx++], 46 | end: +input[idx++], 47 | trigger: +input[idx++], 48 | triggerEnd: +input[idx++] 49 | }) 50 | } 51 | 52 | return animData 53 | } 54 | 55 | function parseArray (raw, parseFun = parseInt) { 56 | if (raw === '') { 57 | return [] 58 | } 59 | 60 | const input = raw.split(DEFAULT_SEPARATOR) 61 | return input.map(element => parseFun(element)) 62 | } 63 | 64 | function parseMap (raw, parseFunKey = parseInt, parseFunValue = parseInt) { 65 | if (raw === '') { 66 | return [] 67 | } 68 | 69 | const input = raw.split(DEFAULT_SEPARATOR) 70 | const map = [] 71 | for (let i = 0; i < input.length; i += 2) { 72 | map[parseFunKey(input[i])] = parseFunValue(input[i + 1]) 73 | } 74 | return map 75 | } 76 | 77 | function parsebonusData (raw) { 78 | const input = raw.split(',') 79 | return { 80 | value: parseInt(input[0]), 81 | amount: parseInt(input[1]) 82 | } 83 | } 84 | 85 | function parseMessage (raw, messages) { 86 | if (raw === '') { 87 | return 88 | } 89 | 90 | const separatorIndex = raw.indexOf(' ') 91 | const key = +raw.substring(0, separatorIndex) 92 | messages[key] = raw.substring(separatorIndex + 1) 93 | } 94 | 95 | export function parseData (raw, globalData) { 96 | const input = raw.split(MAIN_SEPARATOR) 97 | 98 | const data = { 99 | events: [], 100 | playerSpells: [], 101 | inventories: [], 102 | messages: [] 103 | } 104 | 105 | let idx = 0 106 | let toIdx = parseInt(input[idx]) * 3 + ++idx 107 | while (idx < toIdx) { 108 | const event = parseEvent(input[idx++]) 109 | event.spells = parseSpells(input[idx++]) 110 | event.animData = parseAnimData(input[idx++]) 111 | data.events.push(event) 112 | } 113 | 114 | data.scores = parseArray(input[idx++]) 115 | 116 | toIdx = parseInt(input[idx]) + ++idx 117 | while (idx < toIdx) { 118 | data.playerSpells.push(parseArray(input[idx++])) 119 | } 120 | 121 | data.tomeSpells = parseArray(input[idx++]) 122 | data.deliveries = parseArray(input[idx++]) 123 | 124 | toIdx = parseInt(input[idx]) + ++idx 125 | while (idx < toIdx) { 126 | data.inventories.push(parseArray(input[idx++])) 127 | } 128 | 129 | data.active = parseArray(input[idx++]) 130 | data.stock = parseMap(input[idx++]) 131 | data.bonus = parseMap(input[idx++], parseInt, parsebonusData) 132 | 133 | toIdx = input.length 134 | while (idx < toIdx) { 135 | parseMessage(input[idx++], data.messages) 136 | } 137 | 138 | return data 139 | } 140 | 141 | export function parseGlobalData (raw) { 142 | const input = raw.split(MAIN_SEPARATOR) 143 | 144 | const data = { 145 | playerSpells: [], 146 | tomeSpells: [], 147 | deliveries: [] 148 | } 149 | 150 | let idx = 0 151 | const toIdx = parseInt(input[idx]) + ++idx 152 | while (idx < toIdx) { 153 | data.playerSpells.push(parseSpells(input[idx++])) 154 | } 155 | 156 | data.tomeSpells = parseSpells(input[idx++]) 157 | data.deliveries = parseSpells(input[idx++]) 158 | 159 | return data 160 | } 161 | -------------------------------------------------------------------------------- /src/main/resources/view/graphics/ViewModule.js: -------------------------------------------------------------------------------- 1 | import { EV_LEARN, EV_NEW_POTIONS, EV_NEW_TOME_SPELLS } from './gameConstants.js' 2 | import { getRenderer, flagForDestructionOnReinit } from '../core/rendering.js' 3 | import { parseData, parseGlobalData } from './Deserializer.js' 4 | import { updateInventories } from './inventoryUpdate.js' 5 | import { updateScore, updatePotions, updateSpeech } from './miscUpdate.js' 6 | import { updateTomeSpells, updatePlayerSpells, resetSpells, updateDeliveries, updateBonus } from './spellUpdate.js' 7 | import { updateWitches } from './witchUpdate.js' 8 | import * as assets from './assetConstants.js' 9 | import * as layers from './layers.js' 10 | 11 | /* global PIXI */ 12 | 13 | const api = { 14 | viewModule: null, 15 | options: { 16 | debugMode: false, 17 | hideExhaustedSpells: true, 18 | 19 | // Messages 20 | showOthersMessages: true, 21 | meInGame: false, 22 | showMyMessages: true 23 | }, 24 | setDebug: () => {}, 25 | updateSpells: (api) => { 26 | updatePlayerSpells.bind(api.viewModule)(api.previousData, api.currentData, api.progress) 27 | } 28 | } 29 | export { api } 30 | 31 | export class ViewModule { 32 | constructor (assets) { 33 | this.states = [] 34 | this.globalData = {} 35 | this.pool = {} 36 | window.module = this 37 | api.viewModule = this 38 | this.api = api 39 | } 40 | 41 | static get name () { 42 | return 'graphics' 43 | } 44 | 45 | getFromPool (type) { 46 | if (!this.pool[type]) { 47 | this.pool[type] = [] 48 | } 49 | 50 | for (const e of this.pool[type]) { 51 | if (!e.busy) { 52 | e.busy = true 53 | return e 54 | } 55 | } 56 | 57 | const e = this.createEffect(type) 58 | this.pool[type].push(e) 59 | e.busy = true 60 | return e 61 | } 62 | 63 | createEffect (type) { 64 | if (type === 'ingredient') { 65 | const graphics = new PIXI.Sprite() 66 | this.ingredientLayer.addChildAt(graphics, 0) 67 | graphics.anchor.set(0.5) 68 | 69 | return graphics 70 | } else if (type === 'potion') { 71 | const graphics = PIXI.extras.AnimatedSprite.fromFrames(assets.FRAMES_BOTTLE_SPAWN) 72 | graphics.anchor.set(229 / 498, 391 / 524) 73 | graphics.scale.set(1 / 0.7) 74 | this.potionLayer.addChild(graphics) 75 | 76 | return graphics 77 | } else if (type === 'potioff') { 78 | const graphics = PIXI.extras.AnimatedSprite.fromFrames(assets.FRAMES_BOTTLE_DESPAWN) 79 | graphics.anchor.set(219 / 419, 339 / 412) 80 | graphics.scale.set(1 / 0.7) 81 | this.potionLayer.addChild(graphics) 82 | 83 | return graphics 84 | } else if (type === 'splash') { 85 | const graphics = PIXI.extras.AnimatedSprite.fromFrames(assets.FRAMES_SPLASH) 86 | graphics.anchor.set(0.5, 1) 87 | graphics.scale.set(2) 88 | this.fxLayer.addChild(graphics) 89 | 90 | return graphics 91 | } else if (type === 'bonus') { 92 | const text = new PIXI.Text('', { 93 | fontFamily: 'Arial Black, Arial', 94 | fontWeight: 900, 95 | fill: 'white', 96 | fontSize: 72, 97 | stroke: '#0000004C', 98 | strokeThickness: 12 99 | }) 100 | text.position.set(7, 150) 101 | text.anchor.set(0.5) 102 | const container = new PIXI.Container() 103 | container.addChild(text) 104 | this.hudLayer.addChild(container) 105 | container.scale.set(0.4) 106 | 107 | const bonus = { 108 | container, 109 | text 110 | } 111 | 112 | Object.defineProperty(bonus, 'visible', { 113 | set: (value) => { 114 | container.visible = value 115 | }, 116 | get: () => { 117 | return container.visible 118 | } 119 | }) 120 | 121 | return bonus 122 | } else { 123 | return new PIXI.Container() 124 | } 125 | } 126 | 127 | asLayer (func) { 128 | const layer = new PIXI.Container() 129 | func.bind(this)(layer) 130 | return layer 131 | } 132 | 133 | updateScene (previousData, currentData, progress, playerSpeed) { 134 | this.playerSpeed = playerSpeed || 0 135 | this.resetEffects() 136 | 137 | resetSpells.bind(this)() 138 | 139 | updateInventories.bind(this)(previousData, currentData, progress) 140 | updateTomeSpells.bind(this)(previousData, currentData, progress) 141 | updatePlayerSpells.bind(this)(previousData, currentData, progress) 142 | updateDeliveries.bind(this)(previousData, currentData, progress) 143 | updateBonus.bind(this)(previousData, currentData, progress) 144 | updateWitches.bind(this)(previousData, currentData, progress) 145 | updatePotions.bind(this)(previousData, currentData, progress) 146 | updateScore.bind(this)(previousData, currentData, progress) 147 | updateSpeech.bind(this)(previousData, currentData, progress) 148 | 149 | api.previousData = previousData 150 | api.currentData = currentData 151 | api.progress = progress 152 | } 153 | 154 | resetEffects () { 155 | for (const type in this.pool) { 156 | for (const effect of this.pool[type]) { 157 | effect.visible = false 158 | effect.busy = false 159 | } 160 | } 161 | } 162 | 163 | handleFrameData (frameInfo, raw) { 164 | const data = parseData(raw, this.globalData) 165 | 166 | const previous = this.states[this.states.length - 1] 167 | 168 | for (const event of data.events) { 169 | if (event.type === EV_LEARN) { 170 | this.addSpell({ 171 | ...this.spellMap[event.spellId], 172 | id: event.resultId 173 | }) 174 | } else if (event.type === EV_NEW_POTIONS) { 175 | event.spells.forEach(spell => this.addSpell(spell)) 176 | } else if (event.type === EV_NEW_TOME_SPELLS) { 177 | event.spells.forEach(spell => this.addSpell(spell)) 178 | } 179 | 180 | if (event.animData != null) { 181 | for (let i = 0; i < event.animData.length; ++i) { 182 | var ev = event.animData[i] 183 | ev.start /= frameInfo.frameDuration 184 | ev.end /= frameInfo.frameDuration 185 | 186 | if (ev.trigger != null) { 187 | ev.trigger /= frameInfo.frameDuration 188 | ev.triggerEnd /= frameInfo.frameDuration 189 | } 190 | } 191 | } 192 | } 193 | 194 | const state = { 195 | events: data.events, 196 | ...data, 197 | active: new Set(data.active) 198 | } 199 | 200 | state.previous = previous || state 201 | this.states.push(state) 202 | return state 203 | } 204 | 205 | registerAnim (animArr, timeArr, timeIdx, duration) { 206 | animArr.push({ 207 | start: timeArr[timeIdx], 208 | end: timeArr[timeIdx] + duration 209 | }) 210 | timeArr[timeIdx] += duration 211 | } 212 | 213 | newLayer () { 214 | return new PIXI.Container() 215 | } 216 | 217 | reinitScene (container, canvasData) { 218 | this.oversampling = canvasData.oversampling 219 | this.container = container 220 | this.pool = {} 221 | this.spells = {} 222 | 223 | this.backgroundLayer = this.asLayer(layers.initBackground) 224 | this.frontsCauldronsLayer = this.asLayer(layers.initFrontsCauldrons) 225 | this.witchLayer = this.asLayer(layers.initWitches) 226 | this.bubblingLayer = this.asLayer(layers.initBubblings) 227 | this.fxLayer = this.newLayer() 228 | this.ingredientLayer = this.newLayer() 229 | this.potionLayer = this.newLayer() 230 | this.tomeLayer = this.newLayer() 231 | this.playerSpellLayer = this.newLayer() 232 | this.deliveriesLayer = this.newLayer() 233 | this.hudLayer = this.asLayer(layers.initHud) 234 | this.messageLayer = this.asLayer(layers.initSpeech) 235 | this.witchFrontLayer = this.newLayer() 236 | 237 | this.container.addChild(this.backgroundLayer) 238 | this.container.addChild(this.witchLayer) 239 | this.container.addChild(this.bubblingLayer) 240 | this.container.addChild(this.tomeLayer) 241 | this.container.addChild(this.ingredientLayer) 242 | this.container.addChild(this.fxLayer) 243 | this.container.addChild(this.frontsCauldronsLayer) 244 | this.container.addChild(this.playerSpellLayer) 245 | this.container.addChild(this.witchFrontLayer) 246 | this.container.addChild(this.deliveriesLayer) 247 | this.container.addChild(this.potionLayer) 248 | this.container.addChild(this.hudLayer) 249 | this.container.addChild(this.messageLayer) 250 | 251 | this.backgroundLayer.interactiveChildren = false 252 | this.frontsCauldronsLayer.interactiveChildren = false 253 | this.witchLayer.interactiveChildren = false 254 | this.bubblingLayer.interactiveChildren = false 255 | this.fxLayer.interactiveChildren = false 256 | this.ingredientLayer.interactiveChildren = false 257 | this.potionLayer.interactiveChildren = false 258 | this.tomeLayer.interactiveChildren = true 259 | this.playerSpellLayer.interactiveChildren = true 260 | this.deliveriesLayer.interactiveChildren = true 261 | this.hudLayer.interactiveChildren = false 262 | this.messageLayer.interactiveChildren = false 263 | this.witchFrontLayer.interactiveChildren = false 264 | } 265 | 266 | generateTexture (graphics) { 267 | var tex = getRenderer().generateTexture(graphics) 268 | flagForDestructionOnReinit(tex) 269 | return tex 270 | } 271 | 272 | animateScene (delta) { 273 | for (let idx = 0; idx < this.globalData.playerCount; ++idx) { 274 | const { show, container } = this.bubbles[idx] 275 | const stepFactor = Math.pow(0.993 + (0.007 * (this.playerSpeed || 1) / 10), delta) 276 | const targetAlpha = show ? 1 : 0 277 | if (targetAlpha === 1) { 278 | container.alpha = 1 279 | } else { 280 | container.alpha = container.alpha * stepFactor + targetAlpha * (1 - stepFactor) 281 | } 282 | } 283 | } 284 | 285 | handleGlobalData (players, raw) { 286 | const globalData = parseGlobalData(raw) 287 | 288 | this.globalData = { 289 | players: players, 290 | playerCount: players.length 291 | } 292 | 293 | this.spellMap = {} 294 | // Game reconstruction vars 295 | this.globalData.initialSpells = { 296 | deliveries: globalData.deliveries.map(spell => spell.id), 297 | playerSpells: globalData.playerSpells.map(spells => spells.map(spell => spell.id)), 298 | tomeSpells: globalData.tomeSpells.map(spell => spell.id) 299 | } 300 | this.globalData.startingIngredientCount = globalData.startingIngredientCount 301 | 302 | const newSpells = [ 303 | ...globalData.deliveries, 304 | ...globalData.playerSpells[0], 305 | ...globalData.playerSpells[1], 306 | ...globalData.tomeSpells 307 | ] 308 | 309 | newSpells.forEach((spell) => this.addSpell(spell)) 310 | 311 | api.options.meInGame = !!players.find(p => p.isMe) 312 | api.globalData = globalData 313 | } 314 | 315 | addSpell (spell) { 316 | this.spellMap[spell.id] = spell 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /src/main/resources/view/graphics/assetConstants.js: -------------------------------------------------------------------------------- 1 | import { WIDTH } from '../core/constants.js' 2 | import * as utils from '../core/utils.js' 3 | 4 | const INVENTORY_RECT = { 5 | 1: { 6 | topLeft: { x: 1249, y: 192 }, 7 | topRight: { x: 1536, y: 218 }, 8 | bottomLeft: { x: 1241, y: 274 }, 9 | bottomRight: { x: 1527, y: 301 } 10 | }, 11 | 0: { } 12 | } 13 | 14 | INVENTORY_RECT[0] = { 15 | topRight: { 16 | x: WIDTH - INVENTORY_RECT[1].topLeft.x, 17 | y: INVENTORY_RECT[1].topLeft.y 18 | }, 19 | topLeft: { 20 | x: WIDTH - INVENTORY_RECT[1].topRight.x, 21 | y: INVENTORY_RECT[1].topRight.y 22 | }, 23 | bottomRight: { 24 | x: WIDTH - INVENTORY_RECT[1].bottomLeft.x, 25 | y: INVENTORY_RECT[1].bottomLeft.y 26 | }, 27 | bottomLeft: { 28 | x: WIDTH - INVENTORY_RECT[1].bottomRight.x, 29 | y: INVENTORY_RECT[1].bottomRight.y 30 | } 31 | } 32 | 33 | export const INVENTORY_POSITION = { 34 | 0: [], 35 | 1: [] 36 | } 37 | 38 | for (let playerIdx = 0; playerIdx < 2; ++playerIdx) { 39 | const rows = [ 40 | { 41 | from: 'topLeft', 42 | to: 'topRight' 43 | }, 44 | { 45 | from: 'bottomLeft', 46 | to: 'bottomRight' 47 | } 48 | ] 49 | 50 | for (const row of rows) { 51 | const a = INVENTORY_RECT[playerIdx][row.from] 52 | const b = INVENTORY_RECT[playerIdx][row.to] 53 | for (let ingredientIdx = 0; ingredientIdx < 5; ++ingredientIdx) { 54 | const p = utils.lerpPosition(a, b, ingredientIdx / 4) 55 | INVENTORY_POSITION[playerIdx].push(p) 56 | } 57 | } 58 | // Temporary safeguard 59 | for (const row of rows) { 60 | const a = INVENTORY_RECT[playerIdx][row.from] 61 | const b = INVENTORY_RECT[playerIdx][row.to] 62 | for (let ingredientIdx = 0; ingredientIdx < 5; ++ingredientIdx) { 63 | const p = utils.lerpPosition(a, b, ingredientIdx / 4) 64 | INVENTORY_POSITION[playerIdx].push({ x: p.x, y: p.y + 40 }) 65 | } 66 | } 67 | } 68 | 69 | export const TOME_STOCKS_POSITION = [] 70 | 71 | const STOCK_X = 1066 72 | const STOCK_Y = 451 73 | const STOCK_GAP = 82 74 | 75 | for (let i = 0; i < 6; i++) { 76 | TOME_STOCKS_POSITION.push({ x: STOCK_X, y: STOCK_Y - STOCK_GAP * i }) 77 | } 78 | 79 | export const TOME_INGREDIENT_SIZE = 30 80 | export const DELIVERY_INGREDIENT_SIZE = 55 81 | export const INVENTORY_INGREDIENT_SIZE = 75 82 | 83 | export const SPEECH_WIDTH = 452 84 | export const SPEECH_HEIGHT = 100 85 | 86 | export const OBLIVION_POSITION = { 87 | x: WIDTH / 2, 88 | y: -200 89 | } 90 | 91 | const POT_OFFSET_X = 630 92 | const POT_Y = 780 93 | export const POT_POSITION = [ 94 | { 95 | x: POT_OFFSET_X, 96 | y: POT_Y 97 | }, 98 | { 99 | x: WIDTH - POT_OFFSET_X, 100 | y: POT_Y 101 | } 102 | ] 103 | 104 | export const FRAMES_LEARN = { 105 | 0: [ 106 | 'ANIM_LECTURE0001', 107 | 'ANIM_LECTURE0003', 108 | 'ANIM_LECTURE0005', 109 | 'ANIM_LECTURE0007', 110 | 'ANIM_LECTURE0009', 111 | 'ANIM_LECTURE0011', 112 | 'ANIM_LECTURE0013', 113 | 'ANIM_LECTURE0015', 114 | 'ANIM_LECTURE0017', 115 | 'ANIM_LECTURE0019', 116 | 'ANIM_LECTURE0021', 117 | 'ANIM_LECTURE0023', 118 | 'ANIM_LECTURE0025', 119 | 'ANIM_LECTURE0027', 120 | 'ANIM_LECTURE0029', 121 | 'ANIM_LECTURE0031', 122 | 'ANIM_LECTURE0033', 123 | 'ANIM_LECTURE0035', 124 | 'ANIM_LECTURE0037', 125 | 'ANIM_LECTURE0039', 126 | 'ANIM_LECTURE0041', 127 | 'ANIM_LECTURE0043', 128 | 'ANIM_LECTURE0045', 129 | 'ANIM_LECTURE0047', 130 | 'ANIM_LECTURE0049', 131 | 'ANIM_LECTURE0051', 132 | 'ANIM_LECTURE0053', 133 | 'ANIM_LECTURE0055', 134 | 'ANIM_LECTURE0057', 135 | 'ANIM_LECTURE0059', 136 | 'ANIM_LECTURE0061', 137 | 'ANIM_LECTURE0063', 138 | 'ANIM_LECTURE0065', 139 | 'ANIM_LECTURE0067', 140 | 'ANIM_LECTURE0069', 141 | 'ANIM_LECTURE0071', 142 | 'ANIM_LECTURE0073', 143 | 'ANIM_LECTURE0075' 144 | ], 145 | 1: ['ANIM_LECTURE_R0001', 'ANIM_LECTURE_R0003', 'ANIM_LECTURE_R0005', 'ANIM_LECTURE_R0007', 'ANIM_LECTURE_R0009', 'ANIM_LECTURE_R0011', 'ANIM_LECTURE_R0013', 'ANIM_LECTURE_R0015', 'ANIM_LECTURE_R0017', 'ANIM_LECTURE_R0019', 'ANIM_LECTURE_R0021', 'ANIM_LECTURE_R0023', 'ANIM_LECTURE_R0025', 'ANIM_LECTURE_R0027', 'ANIM_LECTURE_R0029', 'ANIM_LECTURE_R0031', 'ANIM_LECTURE_R0033', 'ANIM_LECTURE_R0035', 'ANIM_LECTURE_R0037', 'ANIM_LECTURE_R0039', 'ANIM_LECTURE_R0041', 'ANIM_LECTURE_R0043', 'ANIM_LECTURE_R0045', 'ANIM_LECTURE_R0047', 'ANIM_LECTURE_R0049', 'ANIM_LECTURE_R0051', 'ANIM_LECTURE_R0053', 'ANIM_LECTURE_R0055', 'ANIM_LECTURE_R0057', 'ANIM_LECTURE_R0059', 'ANIM_LECTURE_R0061', 'ANIM_LECTURE_R0063', 'ANIM_LECTURE_R0065', 'ANIM_LECTURE_R0067', 'ANIM_LECTURE_R0069', 'ANIM_LECTURE_R0071', 'ANIM_LECTURE_R0073', 'ANIM_LECTURE_R0075'] 146 | } 147 | 148 | export const FRAMES_STIR = { 149 | 0: [ 150 | 'ANIM_TOUILLE0001', 151 | 'ANIM_TOUILLE0003', 152 | 'ANIM_TOUILLE0005', 153 | 'ANIM_TOUILLE0007', 154 | 'ANIM_TOUILLE0009', 155 | 'ANIM_TOUILLE0011', 156 | 'ANIM_TOUILLE0013', 157 | 'ANIM_TOUILLE0015', 158 | 'ANIM_TOUILLE0017', 159 | 'ANIM_TOUILLE0019', 160 | 'ANIM_TOUILLE0021', 161 | 'ANIM_TOUILLE0023', 162 | 'ANIM_TOUILLE0025', 163 | 'ANIM_TOUILLE0027', 164 | 'ANIM_TOUILLE0029', 165 | 'ANIM_TOUILLE0031', 166 | 'ANIM_TOUILLE0033', 167 | 'ANIM_TOUILLE0035', 168 | 'ANIM_TOUILLE0037', 169 | 'ANIM_TOUILLE0039', 170 | 'ANIM_TOUILLE0041', 171 | 'ANIM_TOUILLE0043', 172 | 'ANIM_TOUILLE0045', 173 | 'ANIM_TOUILLE0047', 174 | 'ANIM_TOUILLE0049', 175 | 'ANIM_TOUILLE0051', 176 | 'ANIM_TOUILLE0053', 177 | 'ANIM_TOUILLE0055', 178 | 'ANIM_TOUILLE0057', 179 | 'ANIM_TOUILLE0059' 180 | ], 181 | 1: ['ANIM_TOUILLE_R0001', 'ANIM_TOUILLE_R0003', 'ANIM_TOUILLE_R0005', 'ANIM_TOUILLE_R0007', 'ANIM_TOUILLE_R0009', 'ANIM_TOUILLE_R0011', 'ANIM_TOUILLE_R0013', 'ANIM_TOUILLE_R0015', 'ANIM_TOUILLE_R0017', 'ANIM_TOUILLE_R0019', 'ANIM_TOUILLE_R0021', 'ANIM_TOUILLE_R0023', 'ANIM_TOUILLE_R0025', 'ANIM_TOUILLE_R0027', 'ANIM_TOUILLE_R0029', 'ANIM_TOUILLE_R0031', 'ANIM_TOUILLE_R0033', 'ANIM_TOUILLE_R0035', 'ANIM_TOUILLE_R0037', 'ANIM_TOUILLE_R0039', 'ANIM_TOUILLE_R0041', 'ANIM_TOUILLE_R0043', 'ANIM_TOUILLE_R0045', 'ANIM_TOUILLE_R0047', 'ANIM_TOUILLE_R0049', 'ANIM_TOUILLE_R0051', 'ANIM_TOUILLE_R0053', 'ANIM_TOUILLE_R0055', 'ANIM_TOUILLE_R0057', 'ANIM_TOUILLE_R0059'] 182 | } 183 | 184 | export const FRAMES_SPLASH = ['ANIM_PLOUF0001', 'ANIM_PLOUF0002', 'ANIM_PLOUF0003', 'ANIM_PLOUF0004', 'ANIM_PLOUF0005', 'ANIM_PLOUF0006', 'ANIM_PLOUF0007', 'ANIM_PLOUF0008', 'ANIM_PLOUF0009', 'ANIM_PLOUF0010', 'ANIM_PLOUF0011', 'ANIM_PLOUF0012', 'ANIM_PLOUF0013', 'ANIM_PLOUF0014', 'ANIM_PLOUF0015', 'ANIM_PLOUF0016', 'ANIM_PLOUF0017', 'ANIM_PLOUF0018', 'ANIM_PLOUF0019', 'ANIM_PLOUF0020', 'ANIM_PLOUF0021', 'ANIM_PLOUF0022', 'ANIM_PLOUF0023', 'ANIM_PLOUF0024', 'ANIM_PLOUF0025', 'ANIM_PLOUF0026', 'ANIM_PLOUF0027', 'ANIM_PLOUF0028', 'ANIM_PLOUF0029', 'ANIM_PLOUF0030', 'ANIM_PLOUF0031', 'ANIM_PLOUF0032', 'ANIM_PLOUF0033', 'ANIM_PLOUF0034', 'ANIM_PLOUF0035', 'ANIM_PLOUF0036', 'ANIM_PLOUF0037', 'ANIM_PLOUF0038', 'ANIM_PLOUF0039', 'ANIM_PLOUF0040', 'ANIM_PLOUF0041', 'ANIM_PLOUF0042', 'ANIM_PLOUF0043', 'ANIM_PLOUF0044', 'ANIM_PLOUF0045'] 185 | export const FRAMES_BLINK = { 186 | 0: ['ANIME_EYES0001', 'ANIME_EYES0002', 'ANIME_EYES0003', 'ANIME_EYES0002'], 187 | 1: ['ANIME_EYES_R0001', 'ANIME_EYES_R0002', 'ANIME_EYES_R0003', 'ANIME_EYES_R0002'] 188 | } 189 | 190 | export const FRAMES_RUPEE_SHINE = ['ANIM_Diams0001', 'ANIM_Diams0003', 'ANIM_Diams0005', 'ANIM_Diams0007', 'ANIM_Diams0009', 'ANIM_Diams0011', 'ANIM_Diams0013', 'ANIM_Diams0015', 'ANIM_Diams0017', 'ANIM_Diams0019', 'ANIM_Diams0021', 'ANIM_Diams0023', 'ANIM_Diams0025'] 191 | 192 | export const FRAMES_BOTTLE_SPAWN = [ 193 | 'APP_BOUTEILLE_10001', 194 | 'APP_BOUTEILLE_10003', 195 | 'APP_BOUTEILLE_10005', 196 | 'APP_BOUTEILLE_10007', 197 | 'APP_BOUTEILLE_10009', 198 | 'APP_BOUTEILLE_10011', 199 | 'APP_BOUTEILLE_10013', 200 | 'APP_BOUTEILLE_10015', 201 | 'APP_BOUTEILLE_10017', 202 | 'APP_BOUTEILLE_10019', 203 | 'APP_BOUTEILLE_10021', 204 | 'APP_BOUTEILLE_10023', 205 | 'APP_BOUTEILLE_10025', 206 | 'APP_BOUTEILLE_10027', 207 | 'APP_BOUTEILLE_10029', 208 | 'APP_BOUTEILLE_10031', 209 | 'APP_BOUTEILLE_10033', 210 | 'APP_BOUTEILLE_10035', 211 | 'APP_BOUTEILLE_10037', 212 | 'APP_BOUTEILLE_10039' 213 | ] 214 | export const FRAMES_BOTTLE_DESPAWN = [ 215 | 'ANIM_DISP_BOUTEILLE_10001', 216 | 'ANIM_DISP_BOUTEILLE_10003', 217 | 'ANIM_DISP_BOUTEILLE_10005', 218 | 'ANIM_DISP_BOUTEILLE_10007', 219 | 'ANIM_DISP_BOUTEILLE_10009', 220 | 'ANIM_DISP_BOUTEILLE_10011', 221 | 'ANIM_DISP_BOUTEILLE_10013', 222 | 'ANIM_DISP_BOUTEILLE_10015', 223 | 'ANIM_DISP_BOUTEILLE_10017', 224 | 'ANIM_DISP_BOUTEILLE_10019', 225 | 'ANIM_DISP_BOUTEILLE_10021', 226 | 'ANIM_DISP_BOUTEILLE_10023', 227 | 'ANIM_DISP_BOUTEILLE_10025', 228 | 'ANIM_DISP_BOUTEILLE_10027', 229 | 'ANIM_DISP_BOUTEILLE_10029', 230 | 'ANIM_DISP_BOUTEILLE_10031', 231 | 'ANIM_DISP_BOUTEILLE_10033', 232 | 'ANIM_DISP_BOUTEILLE_10035', 233 | 'ANIM_DISP_BOUTEILLE_10037', 234 | 'ANIM_DISP_BOUTEILLE_10039', 235 | 'ANIM_DISP_BOUTEILLE_10040', 236 | 'ANIM_DISP_BOUTEILLE_10042', 237 | 'ANIM_DISP_BOUTEILLE_10044', 238 | 'ANIM_DISP_BOUTEILLE_10046' 239 | ] 240 | 241 | export const POT_BUBBLES_HEIGHT = 60 242 | export const POT_BUBBLES_WIDTH = 360 243 | const POT_BUBBLES_OFFSET = -45 244 | export const POT_BUBBLES_POSITION = [ 245 | { 246 | x: POT_POSITION[0].x, 247 | y: POT_POSITION[0].y + POT_BUBBLES_OFFSET 248 | }, 249 | { 250 | x: POT_POSITION[1].x, 251 | y: POT_POSITION[1].y + POT_BUBBLES_OFFSET 252 | } 253 | ] 254 | export const FRAMES_POT = ['ANIM_BUBLES0004', 'ANIM_BUBLES0005', 'ANIM_BUBLES0006', 'ANIM_BUBLES0007', 'ANIM_BUBLES0008', 'ANIM_BUBLES0009', 'ANIM_BUBLES0010', 'ANIM_BUBLES0011', 'ANIM_BUBLES0012', 'ANIM_BUBLES0013', 'ANIM_BUBLES0014', 'ANIM_BUBLES0015', 'ANIM_BUBLES0016', 'ANIM_BUBLES0017', 'ANIM_BUBLES0018', 'ANIM_BUBLES0019', 'ANIM_BUBLES0020', 'ANIM_BUBLES0021', 'ANIM_BUBLES0022', 'ANIM_BUBLES0023', 'ANIM_BUBLES0024', 'ANIM_BUBLES0025', 'ANIM_BUBLES0026', 'ANIM_BUBLES0027', 'ANIM_BUBLES0028', 'ANIM_BUBLES0029', 'ANIM_BUBLES0030', 'ANIM_BUBLES0031', 'ANIM_BUBLES0032', 'ANIM_BUBLES0033', 'ANIM_BUBLES0034', 'ANIM_BUBLES0035', 'ANIM_BUBLES0036', 'ANIM_BUBLES0037', 'ANIM_BUBLES0038', 'ANIM_BUBLES0039', 'ANIM_BUBLES0040', 'ANIM_BUBLES0041', 'ANIM_BUBLES0042', 'ANIM_BUBLES0043', 'ANIM_BUBLES0044'] 255 | 256 | export const FRAMES_REST = { 257 | 0: [ 258 | 'ANIM_CANALYSE0001', 259 | 'ANIM_CANALYSE0003', 260 | 'ANIM_CANALYSE0005', 261 | 'ANIM_CANALYSE0007', 262 | 'ANIM_CANALYSE0009', 263 | 'ANIM_CANALYSE0011', 264 | 'ANIM_CANALYSE0013', 265 | 'ANIM_CANALYSE0015', 266 | 'ANIM_CANALYSE0017', 267 | 'ANIM_CANALYSE0019', 268 | 'ANIM_CANALYSE0021', 269 | 'ANIM_CANALYSE0023', 270 | 'ANIM_CANALYSE0025', 271 | 'ANIM_CANALYSE0027', 272 | 'ANIM_CANALYSE0029', 273 | 'ANIM_CANALYSE0031', 274 | 'ANIM_CANALYSE0033', 275 | 'ANIM_CANALYSE0035', 276 | 'ANIM_CANALYSE0037', 277 | 'ANIM_CANALYSE0039', 278 | 'ANIM_CANALYSE0041', 279 | 'ANIM_CANALYSE0043', 280 | 'ANIM_CANALYSE0045', 281 | 'ANIM_CANALYSE0047', 282 | 'ANIM_CANALYSE0049' 283 | ], 284 | 1: ['ANIM_CANALYSE_R0001', 'ANIM_CANALYSE_R0003', 'ANIM_CANALYSE_R0005', 'ANIM_CANALYSE_R0007', 'ANIM_CANALYSE_R0009', 'ANIM_CANALYSE_R0011', 'ANIM_CANALYSE_R0013', 'ANIM_CANALYSE_R0015', 'ANIM_CANALYSE_R0017', 'ANIM_CANALYSE_R0019', 'ANIM_CANALYSE_R0021', 'ANIM_CANALYSE_R0023', 'ANIM_CANALYSE_R0025', 'ANIM_CANALYSE_R0027', 'ANIM_CANALYSE_R0029', 'ANIM_CANALYSE_R0031', 'ANIM_CANALYSE_R0033', 'ANIM_CANALYSE_R0035', 'ANIM_CANALYSE_R0037', 'ANIM_CANALYSE_R0039', 'ANIM_CANALYSE_R0041', 'ANIM_CANALYSE_R0043', 'ANIM_CANALYSE_R0045', 'ANIM_CANALYSE_R0047', 'ANIM_CANALYSE_R0049'] 285 | } 286 | -------------------------------------------------------------------------------- /src/main/resources/view/graphics/gameConstants.js: -------------------------------------------------------------------------------- 1 | export const EV_LEARN = 0 2 | export const EV_NEW_POTIONS = 1 3 | export const EV_NEW_TOME_SPELLS = 2 4 | export const EV_CAST = 3 5 | export const EV_BREW = 4 6 | export const EV_REST = 5 7 | export const EV_LEARN_PAY = 6 8 | 9 | export const TYPE_PLAYER = 'CAST' 10 | export const TYPE_TOME = 'LEARN' 11 | export const TYPE_DELIVERY = 'BREW' 12 | -------------------------------------------------------------------------------- /src/main/resources/view/graphics/layers.js: -------------------------------------------------------------------------------- 1 | import { setAnimationProgress, fit } from './utils.js' 2 | import { WIDTH, HEIGHT } from '../core/constants.js' 3 | import * as assets from './assetConstants.js' 4 | import * as utils from '../core/utils.js' 5 | /* globals PIXI */ 6 | 7 | export function initBackground (layer) { 8 | const back = PIXI.Sprite.fromFrame('Background.jpg') 9 | fit(back, WIDTH, HEIGHT) 10 | layer.addChild(back) 11 | } 12 | 13 | const WITCH_OFFSET_X = 509 14 | const WITCH_Y = 608 15 | 16 | const BLINK_X = 0 17 | const BLINK_Y = 0 18 | const BLINK_SPEED = 0.2 19 | const BLINK_DELAY_SPEED = 1 / 200 20 | 21 | const STIR_X = 32 22 | const STIR_Y = -27 23 | 24 | const LEARN_X = 65 25 | const LEARN_Y = -14 26 | const LEARN_SPEED = 0.45 27 | 28 | const REST_X = -53 29 | const REST_Y = -69 30 | const REST_SPEED = 0.45 31 | 32 | export function initWitches (layer) { 33 | this.witches = [] 34 | for (let idx = 0; idx < this.globalData.playerCount; ++idx) { 35 | const flip = idx === 0 ? 1 : -1 36 | 37 | const idle = PIXI.extras.AnimatedSprite.fromFrames(assets.FRAMES_BLINK[idx]) 38 | const stir = PIXI.extras.AnimatedSprite.fromFrames(assets.FRAMES_STIR[idx]) 39 | const learn = PIXI.extras.AnimatedSprite.fromFrames(assets.FRAMES_LEARN[idx]) 40 | const rest = PIXI.extras.AnimatedSprite.fromFrames(assets.FRAMES_REST[idx]) 41 | 42 | idle.position.set(BLINK_X, BLINK_Y) 43 | stir.position.set(STIR_X, STIR_Y) 44 | learn.position.set(LEARN_X, LEARN_Y) 45 | rest.position.set(REST_X, REST_Y) 46 | 47 | idle.gotoAndPlay(Math.random()) 48 | const animationSpeedMod = Math.random() 49 | Object.defineProperty(idle, 'animationSpeed', { 50 | get: () => Math.floor(idle.currentFrame === 0) ? (BLINK_DELAY_SPEED - (BLINK_DELAY_SPEED / 2) * animationSpeedMod) : BLINK_SPEED 51 | }) 52 | learn.animationSpeed = LEARN_SPEED 53 | rest.animationSpeed = REST_SPEED 54 | 55 | const anims = [idle, stir, learn, rest] 56 | 57 | const container = new PIXI.Container() 58 | container.scale.x = flip 59 | 60 | container.position.set(WIDTH * idx + flip * WITCH_OFFSET_X, WITCH_Y) 61 | 62 | anims.forEach(anim => { 63 | anim.anchor.set(0.5, 0.5) 64 | anim.scale.set(1 / 0.7) 65 | container.addChild(anim) 66 | anim.visible = false 67 | }) 68 | layer.addChild(container) 69 | 70 | this.witches.push({ 71 | idle, stir, learn, rest, anims, container 72 | }) 73 | } 74 | } 75 | 76 | export function initBubblings (layer) { 77 | for (let idx = 0; idx < this.globalData.playerCount; idx++) { 78 | const animation = PIXI.extras.AnimatedSprite.fromFrames(assets.FRAMES_POT) 79 | 80 | animation.position.copy(assets.POT_BUBBLES_POSITION[idx]) 81 | fit(animation, assets.POT_BUBBLES_WIDTH, assets.POT_BUBBLES_HEIGHT) 82 | animation.anchor.set(0.5) 83 | 84 | animation.visible = true 85 | animation.loop = true 86 | 87 | setAnimationProgress(animation, idx / this.globalData.playerCount) 88 | animation.play() 89 | 90 | layer.addChild(animation) 91 | } 92 | } 93 | 94 | const HUD_PANEL_OFFSET_X = 0 95 | const HUD_PANEL_Y = 0 96 | const NAME_ZONE_WIDTH = 430 97 | const NAME_ZONE_HEIGHT = 66 98 | 99 | const NAME_OFFSET_X = 354 100 | const NAME_Y = 35 101 | 102 | const AVATAR_OFFSET_X = 69 103 | const AVATAR_Y = 68 104 | const AVATAR_SIZE = 130 105 | 106 | function wordWrap (text) { 107 | var wordWrapWidth = this._style.wordWrapWidth 108 | var self = this 109 | text = text.replace(/\w+/g, function (text) { 110 | if (self.context.measureText(text).width > wordWrapWidth) { 111 | var list = [] 112 | while (text.length > 0) { 113 | var length = 1 114 | while (length <= text.length && self.context.measureText(text.slice(0, length)).width < wordWrapWidth) { 115 | length++ 116 | } 117 | list.push(text.slice(0, length - 1)) 118 | text = text.slice(length - 1) 119 | } 120 | return list.join('\n') 121 | } 122 | return text 123 | }) 124 | return this._wordWrap(text) 125 | } 126 | 127 | const SPEECH_OFFSET_X = 710 128 | const SPEECH_OFFSET_Y = [-46, 46] 129 | const SPEECH_Y = 700 130 | 131 | export function initSpeech (layer) { 132 | this.bubbles = [] 133 | for (let idx = 0; idx < this.globalData.playerCount; ++idx) { 134 | const player = this.globalData.players[idx] 135 | const flip = idx === 0 ? 1 : -1 136 | 137 | const container = new PIXI.Container() 138 | 139 | const bubble = PIXI.Sprite.fromFrame('dial0.png') 140 | bubble.alpha = 1 141 | bubble.scale.x = flip 142 | bubble.x -= 15 * flip 143 | 144 | bubble.anchor.y = 1 145 | const speech = new PIXI.Text('', { 146 | fontFamily: 'Arial Black, Arial', 147 | fontWeight: 900, 148 | fontSize: 40, 149 | fill: idx === 0 ? '#0000FF' : player.color, 150 | align: 'center', 151 | wordWrap: true, 152 | wordWrapWidth: 300 153 | }) 154 | speech._wordWrap = speech.wordWrap 155 | speech.wordWrap = wordWrap 156 | speech.anchor.set(0.5) 157 | 158 | container.position.set(WIDTH * idx + flip * SPEECH_OFFSET_X, SPEECH_Y + SPEECH_OFFSET_Y[idx]) 159 | container.alpha = 0 160 | container.targetAlpha = 0 161 | bubble.height = assets.SPEECH_HEIGHT 162 | bubble.width = assets.SPEECH_WIDTH 163 | 164 | bubble.alpha = 0.5 165 | 166 | speech.x = (idx === 0 ? bubble.width / 2 : -bubble.width / 2) 167 | speech.y = bubble.height / 2 - 100 168 | 169 | container.addChild(bubble) 170 | container.addChild(speech) 171 | layer.addChild(container) 172 | this.bubbles.push({ container, bubble, speech }) 173 | } 174 | } 175 | 176 | export function initHud (layer) { 177 | this.scores = [] 178 | for (let idx = 0; idx < this.globalData.playerCount; ++idx) { 179 | const player = this.globalData.players[idx] 180 | const flip = idx === 0 ? 1 : -1 181 | 182 | const playerHud = new PIXI.Container() 183 | 184 | const panelContainer = new PIXI.Container() 185 | panelContainer.position.set(WIDTH * idx + flip * HUD_PANEL_OFFSET_X, HUD_PANEL_Y) 186 | 187 | const hudPanel = PIXI.Sprite.fromFrame(`HUD_${idx}`) 188 | hudPanel.anchor.set(idx, 0) 189 | 190 | const rupees = PIXI.Sprite.fromFrame('diams') 191 | 192 | rupees.anchor.set(0.5) 193 | if (idx === 0) { 194 | rupees.position.set(206, 126) 195 | rupees.rotation = 0 196 | } else { 197 | rupees.position.set(1534, 124) 198 | rupees.rotation = -Math.PI / 10 199 | } 200 | 201 | const name = new PIXI.Text(player.name, { 202 | fill: 0xFFFFFF, 203 | fontWeight: 900, 204 | fontSize: 60, 205 | stroke: '#0000004C', 206 | strokeThickness: 10, 207 | fontFamily: 'Arial Black, Arial' 208 | }) 209 | name.anchor.set(0.5, 0.5) 210 | name.x = WIDTH * idx + flip * NAME_OFFSET_X 211 | name.y = NAME_Y 212 | const coeff = utils.fitAspectRatio(name.width, name.height, NAME_ZONE_WIDTH, NAME_ZONE_HEIGHT) 213 | name.scale.set(Math.min(1, coeff)) 214 | 215 | const avatar = new PIXI.Sprite(player.avatar) 216 | avatar.x = WIDTH * idx + flip * AVATAR_OFFSET_X 217 | avatar.y = AVATAR_Y 218 | avatar.anchor.set(0.5) 219 | avatar.width = AVATAR_SIZE 220 | avatar.height = AVATAR_SIZE 221 | 222 | const mask = new PIXI.Graphics() 223 | mask.position.set(WIDTH * idx + flip * AVATAR_OFFSET_X, AVATAR_Y) 224 | mask.beginFill(0, 1) 225 | mask.drawCircle(0, 0, AVATAR_SIZE / 2) 226 | mask.endFill() 227 | avatar.mask = mask 228 | 229 | const score = new PIXI.Text('0', { 230 | fill: 0xFFFFFF, 231 | fontWeight: 900, 232 | fontSize: 60, 233 | stroke: '#0000004C', 234 | strokeThickness: 10, 235 | fontFamily: 'Arial Black, Arial' 236 | }) 237 | score.anchor.set(0, 0.5) 238 | if (idx === 0) { 239 | score.position.set(248, 126) 240 | score.rotation = -0.01 241 | } else { 242 | score.position.set(1576, 124) 243 | score.rotation = 0.03 244 | } 245 | 246 | this.scores.push(score) 247 | panelContainer.addChild(hudPanel) 248 | playerHud.addChild(avatar) 249 | playerHud.addChild(mask) 250 | playerHud.addChild(panelContainer) 251 | playerHud.addChild(rupees) 252 | playerHud.addChild(name) 253 | playerHud.addChild(score) 254 | layer.addChild(playerHud) 255 | } 256 | } 257 | 258 | const FRONTS_CAULDRONS_X = 961 259 | const FRONTS_CAULDRONS_Y = 785 260 | const FRONTS_CAULDRONS_ANCHOR = 0.5 261 | 262 | export function initFrontsCauldrons (layer) { 263 | const fronts = PIXI.Sprite.fromFrame('cauldrons_front.png') 264 | fronts.visible = true 265 | fronts.anchor.set(FRONTS_CAULDRONS_ANCHOR) 266 | fronts.position.x = FRONTS_CAULDRONS_X 267 | fronts.position.y = FRONTS_CAULDRONS_Y 268 | layer.addChild(fronts) 269 | } 270 | -------------------------------------------------------------------------------- /src/main/resources/view/graphics/miscUpdate.js: -------------------------------------------------------------------------------- 1 | import { EV_BREW } from './gameConstants.js' 2 | import { setAnimationProgress } from './utils.js' 3 | import { WIDTH } from '../core/constants.js' 4 | import * as assets from './assetConstants.js' 5 | import * as utils from '../core/utils.js' 6 | 7 | export function updateScore (previous, current, progress) { 8 | previous.scores.forEach((score, idx) => { 9 | this.scores[idx].text = score.toString() 10 | }) 11 | current.events 12 | .filter(ev => ev.type === EV_BREW) 13 | .forEach(ev => { 14 | const animIdx = ev.animData.findIndex(animData => animData.start < progress && progress <= animData.end) 15 | if (animIdx > ev.animData.length - 2 || animIdx === -1) { 16 | this.scores[ev.playerIdx].text = current.scores[ev.playerIdx].toString() 17 | } 18 | }) 19 | } 20 | 21 | const POTION_OFFSET_X = 627 22 | const POTION_Y = 883 23 | const POTION_BOUNCE_HEIGHT = 50 24 | 25 | function bounceLerpPosition (from, to, progress) { 26 | const result = utils.lerpPosition(from, to, progress) 27 | result.y -= POTION_BOUNCE_HEIGHT * Math.abs(Math.sin(Math.PI * progress * 2)) * (Math.cos(progress * Math.PI) + 3) / 4 28 | return result 29 | } 30 | 31 | export function updatePotions (previous, current, progress) { 32 | current.events 33 | .filter(ev => ev.type === EV_BREW) 34 | .forEach(ev => { 35 | const animIdx = ev.animData.findIndex(animData => animData.start < progress && progress <= animData.end) 36 | if (animIdx === ev.animData.length - 3) { 37 | const anim = ev.animData[animIdx] 38 | // POTION spawn 39 | const potion = this.getFromPool('potion') 40 | potion.position.set(ev.playerIdx === 0 ? POTION_OFFSET_X : (WIDTH - POTION_OFFSET_X), POTION_Y) 41 | potion.visible = true 42 | const animP = utils.unlerp(anim.start, anim.end, progress) 43 | setAnimationProgress(potion, animP) 44 | } else if (animIdx === ev.animData.length - 2) { 45 | const anim = ev.animData[animIdx] 46 | // POTION to counter 47 | const potion = this.getFromPool('potion') 48 | const spellContainer = this.spells[ev.spellId].container 49 | const from = { 50 | x: ev.playerIdx === 0 ? POTION_OFFSET_X : (WIDTH - POTION_OFFSET_X), 51 | y: POTION_Y 52 | } 53 | const to = { 54 | x: spellContainer.position.x + spellContainer.width / 2, 55 | y: spellContainer.position.y 56 | } 57 | 58 | potion.visible = true 59 | const animP = utils.unlerp(anim.start, anim.end, progress) 60 | potion.position.copy(bounceLerpPosition(from, to, animP)) 61 | setAnimationProgress(potion, 1) 62 | } else if (animIdx === ev.animData.length - 1) { 63 | const anim = ev.animData[animIdx] 64 | // POTION to counter 65 | const potion = this.getFromPool('potioff') 66 | const spellContainer = this.spells[ev.spellId].container 67 | 68 | potion.position.set( 69 | spellContainer.position.x + spellContainer.width / 2, 70 | spellContainer.position.y 71 | ) 72 | 73 | potion.visible = true 74 | const animP = utils.unlerp(anim.start, anim.end, progress) 75 | setAnimationProgress(potion, animP) 76 | } 77 | }) 78 | } 79 | 80 | const SPEECH_PAD_X = 60 81 | const SPEECH_PAD_Y = -8 82 | 83 | export function updateSpeech (previous, current, progress) { 84 | for (let idx = 0; idx < this.globalData.playerCount; ++idx) { 85 | const { container, speech, bubble } = this.bubbles[idx] 86 | 87 | const message = current.messages[idx] 88 | this.bubbles[idx].show = !!message 89 | const textMaxWidth = assets.SPEECH_WIDTH - SPEECH_PAD_X 90 | const textMaxHeight = assets.SPEECH_HEIGHT - SPEECH_PAD_Y 91 | 92 | speech.scale.set(1) 93 | speech.style.wordWrapWidth = textMaxWidth 94 | if (message) { 95 | speech.text = message 96 | } 97 | if (speech.height > textMaxHeight) { 98 | const scale = utils.fitAspectRatio(speech.width, speech.height, textMaxWidth, textMaxHeight) 99 | speech.scale.set(scale) 100 | speech.style.wordWrapWidth *= 1 / speech.scale.x 101 | } else { 102 | const scale = utils.fitAspectRatio(speech.width, speech.height, textMaxWidth, textMaxHeight) 103 | speech.scale.set(Math.min(1.6, scale)) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/resources/view/graphics/utils.js: -------------------------------------------------------------------------------- 1 | import { TOME_STOCKS_POSITION } from './assetConstants.js' 2 | import * as utils from '../core/utils.js' 3 | 4 | export function setAnimationProgress (fx, progress) { 5 | let idx = Math.floor(progress * fx.totalFrames) 6 | idx = Math.min(fx.totalFrames - 1, idx) 7 | fx.gotoAndStop(idx) 8 | } 9 | 10 | export function fit (entity, maxWidth, maxHeight) { 11 | entity.scale.set(utils.fitAspectRatio(entity.texture.width, entity.texture.height, maxWidth, maxHeight)) 12 | } 13 | 14 | export function getStockPositionFromCenter (center, index, stockCount) { 15 | const increment = 2 * Math.PI / stockCount 16 | const radius = (stockCount === 1 ? 0 : 20) 17 | 18 | const position = { 19 | x: center.x + radius * Math.cos(increment * index), 20 | y: center.y + radius * Math.sin(increment * index) 21 | } 22 | 23 | return position 24 | } 25 | 26 | export function getStockPosition (tomeIdx, index, stockCount) { 27 | const center = TOME_STOCKS_POSITION[tomeIdx] 28 | return getStockPositionFromCenter(center, index, stockCount) 29 | } 30 | 31 | export function applyOffsets (position, widthOffset, heightOffset) { 32 | return { 33 | x: position.x + widthOffset, 34 | y: position.y + heightOffset 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/view/graphics/witchUpdate.js: -------------------------------------------------------------------------------- 1 | import { EV_LEARN, EV_CAST, EV_BREW, EV_REST } from './gameConstants.js' 2 | 3 | function showAnim (witch, anim, speed) { 4 | witch.anims.forEach(anim => { 5 | anim.visible = false 6 | anim.stop() 7 | }) 8 | witch[anim].visible = true 9 | witch[anim].play() 10 | if (speed != null) { 11 | witch[anim].animationSpeed = speed 12 | } 13 | } 14 | 15 | export function updateWitches (previous, current, progress) { 16 | for (let i = 0; i < this.globalData.playerCount; ++i) { 17 | const witch = this.witches[i] 18 | witch.container.parent.removeChild(witch.container) 19 | showAnim(witch, 'idle') 20 | 21 | let parent = this.witchFrontLayer 22 | 23 | current.events 24 | .filter(e => e.playerIdx === i) 25 | .forEach(e => { 26 | if (e.type === EV_LEARN) { 27 | showAnim(witch, 'learn') 28 | } else if (e.type === EV_CAST) { 29 | const animIdx = e.animData.findIndex(animData => animData.start < progress && progress <= animData.end) 30 | if (animIdx >= 0) { 31 | showAnim(witch, 'stir', 0.45) 32 | parent = this.witchLayer 33 | } 34 | } else if (e.type === EV_BREW) { 35 | const animIdx = e.animData.findIndex(animData => animData.start < progress && progress <= animData.end) 36 | if (animIdx >= 0 && animIdx <= e.animData.length - 3) { 37 | showAnim(witch, 'stir', 0.65) 38 | parent = this.witchLayer 39 | } 40 | } else if (e.type === EV_REST) { 41 | showAnim(witch, 'rest') 42 | } 43 | }) 44 | parent.addChild(witch.container) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/resources/view/tooltip/TooltipModule.js: -------------------------------------------------------------------------------- 1 | import { api as ViewModule } from '../graphics/ViewModule.js' 2 | import { HEIGHT } from '../core/constants.js' 3 | 4 | /* global PIXI */ 5 | 6 | const PADDING = 5 7 | const CURSOR_WIDTH = 20 8 | 9 | function generateText (text, size, color, align) { 10 | var textEl = new PIXI.Text(text, { 11 | fontSize: Math.round(size / 1.2) + 'px', 12 | fontFamily: 'Lato', 13 | fontWeight: 'bold', 14 | fill: color 15 | }) 16 | 17 | textEl.lineHeight = Math.round(size / 1.2) 18 | if (align === 'right') { 19 | textEl.anchor.x = 1 20 | } else if (align === 'center') { 21 | textEl.anchor.x = 0.5 22 | } 23 | 24 | return textEl 25 | }; 26 | 27 | function initTooltip (tooltip, text) { 28 | tooltip.label.text = text 29 | 30 | const width = tooltip.label.width + PADDING * 2 31 | const height = tooltip.label.height + PADDING * 2 32 | 33 | tooltip.offset = -width 34 | 35 | tooltip.background.clear() 36 | tooltip.background.beginFill(0x0, 0.8) 37 | tooltip.background.drawRect(0, 0, width, height) 38 | tooltip.background.endFill() 39 | 40 | tooltip.visible = true 41 | } 42 | 43 | export class TooltipModule { 44 | constructor (assets) { 45 | this.interactive = {} 46 | this.previousFrame = { 47 | registrations: {}, 48 | extra: {} 49 | } 50 | this.lastProgress = 1 51 | this.lastFrame = 0 52 | } 53 | 54 | static get name () { 55 | return 'tooltips' 56 | } 57 | 58 | static showTooltip (text) { 59 | initTooltip(ViewModule.tooltip, text) 60 | } 61 | 62 | static moveTooltip (event) { 63 | const newPosition = event.data.getLocalPosition(ViewModule.tooltipContainer) 64 | 65 | let xOffset = ViewModule.tooltip.offset 66 | let yOffset = 0 67 | 68 | if (newPosition.x + xOffset < 0) { 69 | xOffset = CURSOR_WIDTH 70 | } 71 | 72 | if (newPosition.y + ViewModule.tooltip.height > HEIGHT) { 73 | yOffset = HEIGHT - newPosition.y - ViewModule.tooltip.height 74 | } 75 | 76 | ViewModule.tooltip.position.x = newPosition.x + xOffset 77 | ViewModule.tooltip.position.y = newPosition.y + yOffset 78 | } 79 | 80 | static hideTooltip () { 81 | ViewModule.tooltip.visible = false 82 | } 83 | 84 | updateScene (previousData, currentData, progress) { 85 | this.previousFrame = previousData 86 | this.currentFrame = currentData 87 | this.currentProgress = progress 88 | } 89 | 90 | handleFrameData (frameInfo) { 91 | return {} 92 | } 93 | 94 | reinitScene (container, canvasData) { 95 | var tooltip = new PIXI.Container() 96 | var background = new PIXI.Graphics() 97 | var label = generateText('DEFAULT', 36, 0xFFFFFF, 'left') 98 | 99 | label.position.x = PADDING 100 | label.position.y = PADDING 101 | 102 | tooltip.visible = false 103 | tooltip.background = tooltip.addChild(background) 104 | tooltip.label = tooltip.addChild(label) 105 | 106 | ViewModule.tooltip = tooltip 107 | ViewModule.tooltipContainer = container 108 | container.addChild(ViewModule.tooltip) 109 | } 110 | 111 | animateScene (delta) { 112 | 113 | } 114 | 115 | handleGlobalData (players, globalData) { 116 | 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/test/java/BasicAgent.java: -------------------------------------------------------------------------------- 1 | import java.util.*; 2 | import java.io.*; 3 | import java.math.*; 4 | 5 | /** 6 | * Auto-generated code below aims at helping you parse 7 | * the standard input according to the problem statement. 8 | **/ 9 | class BasicAgent { 10 | 11 | public static void main(String args[]) { 12 | Scanner in = new Scanner(System.in); 13 | 14 | // game loop 15 | while (true) { 16 | int actionCount = in.nextInt(); // the number of spells and recipes in play 17 | for (int i = 0; i < actionCount; i++) { 18 | int actionId = in.nextInt(); // the unique ID of this spell or recipe 19 | String actionType = in.next(); // in the first league: BREW; later: CAST, OPPONENT_CAST, LEARN, BREW 20 | int delta0 = in.nextInt(); // tier-0 ingredient change 21 | int delta1 = in.nextInt(); // tier-1 ingredient change 22 | int delta2 = in.nextInt(); // tier-2 ingredient change 23 | int delta3 = in.nextInt(); // tier-3 ingredient change 24 | int price = in.nextInt(); // the price in rupees if this is a potion 25 | int tomeIndex = in.nextInt(); // in the first two leagues: always 0; later: the index in the tome if this is a tome spell, equal to the read-ahead tax 26 | int taxCount = in.nextInt(); // in the first two leagues: always 0; later: the amount of taxed tier-0 ingredients you gain from learning this spell 27 | boolean castable = in.nextInt() != 0; // in the first league: always 0; later: 1 if this is a castable player spell 28 | boolean repeatable = in.nextInt() != 0; // for the first two leagues: always 0; later: 1 if this is a repeatable player spell 29 | } 30 | for (int i = 0; i < 2; i++) { 31 | int inv0 = in.nextInt(); // tier-0 ingredients in inventory 32 | int inv1 = in.nextInt(); 33 | int inv2 = in.nextInt(); 34 | int inv3 = in.nextInt(); 35 | int score = in.nextInt(); // amount of rupees 36 | } 37 | 38 | // Write an action using System.out.println() 39 | // To debug: System.err.println("Debug messages..."); 40 | 41 | 42 | // in the first league: BREW | WAIT; later: BREW | CAST [] | LEARN | REST | WAIT 43 | System.out.println("WAIT"); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/test/java/Fall2020Main.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | 3 | import com.codingame.gameengine.runner.MultiplayerGameRunner; 4 | 5 | public class Fall2020Main { 6 | public static void main(String[] args) throws IOException, InterruptedException { 7 | MultiplayerGameRunner gameRunner = new MultiplayerGameRunner(); 8 | 9 | //Choose league level 10 | gameRunner.setLeagueLevel(1); 11 | 12 | //Add players 13 | gameRunner.addAgent(BasicAgent.class, "Kotake"); 14 | gameRunner.addAgent(BasicAgent.class, "Koume"); 15 | 16 | //Set game seed 17 | gameRunner.setSeed(5842184981578562716L); 18 | 19 | //Run game and start viewer on 'http://localhost:8888/' 20 | gameRunner.start(8888); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | # Configuration 2 | name = PropertiesConfig 3 | status = WARN 4 | 5 | appender.console.type = Console 6 | appender.console.name = CONSOLE 7 | appender.console.layout.type = PatternLayout 8 | appender.console.layout.pattern = [%d{HH:mm:ss}] %-5p : %c{1} - %m%n 9 | 10 | rootLogger.level = info 11 | rootLogger.appenderRef.console.ref = CONSOLE 12 | --------------------------------------------------------------------------------