├── bug.txt
├── src
├── go.mod
├── readme.md
├── dice
│ └── throw_dice.go
└── main
│ └── run_monopoly.go
├── favicon.ico
├── img
├── bijou.png
├── bus.png
├── dice.png
├── dices.png
├── hotel.png
├── house.png
├── info2.png
├── jail.jpg
├── list.png
├── money.png
├── save.png
├── swap.png
├── tap.png
├── tour.png
├── tour2.png
├── train.png
├── banque.png
├── banque2.png
├── house2.png
├── light2.png
├── maison.png
├── money2.png
├── prison.png
├── behaviour.png
├── big_train.png
├── info-user.png
├── info-user2.png
├── strategy.png
├── little_train.png
├── mr_monopoly.jpg
├── mr_monopoly.png
├── reload_dice.png
├── interrogation.png
├── jamesbond
│ ├── woman.png
│ ├── gadget.png
│ ├── spectre.png
│ ├── aston_martin.png
│ └── universal_export.png
└── starwars
│ ├── r2_d2.png
│ ├── xwing.png
│ ├── clone_1.png
│ ├── clone_2.png
│ ├── clone_3.png
│ ├── clone_4.png
│ ├── sw_c3po.png
│ ├── sw_clone.png
│ ├── sw_r2d2.png
│ ├── boba_fett.png
│ ├── darth_vader.png
│ ├── jango_fett.png
│ ├── stormtrooper.png
│ ├── sw_boba_fett.png
│ ├── sw_chewbacca.png
│ ├── jedi_light_saber.png
│ ├── sith_light_saber.png
│ └── sw_darth_vader.png
├── old
├── plateau.jpg
└── monopoly_old.html
├── poc
├── img_test
│ ├── icon_ok_1.png
│ ├── icon_ok_10.png
│ ├── icon_ok_2.png
│ ├── icon_ok_3.png
│ ├── icon_ok_4.png
│ ├── icon_ok_5.png
│ ├── icon_ok_6.png
│ ├── icon_ok_7.png
│ ├── icon_ok_8.png
│ └── icon_ok_9.png
├── pi.js
├── test-eyes.html
├── jquery.mousewheel.min.js
├── slider.html
└── bingo.html
├── data
├── data-monopoly-circle-bond.json
├── data-monopoly-couleur.json
├── data-monopoly-circle-starwars2.json
├── data-monopoly-starwars2.json
├── data-monopoly-circle.json
├── plateaux.json
├── data-monopoly-junior.json
├── data-monopoly-jamesbond.json
├── data-monopoly-starwars.json
├── data-monopoly-original.json
└── data-monopoly.json
├── js
├── request_service.js
├── bus_message.js
├── utils.js
├── entity
│ ├── network
│ │ └── local_joueur.js
│ ├── pion.js
│ ├── cartes_action.js
│ ├── comportement.js
│ └── strategie.js
├── sauvegarde.js
├── core
│ ├── gestion_constructions.js
│ └── gestion_joueurs.js
├── ui
│ ├── graphics.js
│ └── circle_graphics.js
└── display
│ └── displayers.js
├── package.json
├── webpack-conf.js
├── .github
└── workflows
│ └── main.yml
├── README.textile
├── webpack-conf-public.js
├── css
└── qunit-1.12.0.css
├── test
└── test-maisons-js.html
└── lib
└── circletype.min.js
/bug.txt:
--------------------------------------------------------------------------------
1 | 7) Ajout de messages sympas lors de l'echange
2 |
--------------------------------------------------------------------------------
/src/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/jotitan/monopoly
2 |
3 | go 1.12
4 |
--------------------------------------------------------------------------------
/src/readme.md:
--------------------------------------------------------------------------------
1 | Server implementation in golang for remote player.
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/favicon.ico
--------------------------------------------------------------------------------
/img/bijou.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/bijou.png
--------------------------------------------------------------------------------
/img/bus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/bus.png
--------------------------------------------------------------------------------
/img/dice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/dice.png
--------------------------------------------------------------------------------
/img/dices.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/dices.png
--------------------------------------------------------------------------------
/img/hotel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/hotel.png
--------------------------------------------------------------------------------
/img/house.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/house.png
--------------------------------------------------------------------------------
/img/info2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/info2.png
--------------------------------------------------------------------------------
/img/jail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/jail.jpg
--------------------------------------------------------------------------------
/img/list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/list.png
--------------------------------------------------------------------------------
/img/money.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/money.png
--------------------------------------------------------------------------------
/img/save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/save.png
--------------------------------------------------------------------------------
/img/swap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/swap.png
--------------------------------------------------------------------------------
/img/tap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/tap.png
--------------------------------------------------------------------------------
/img/tour.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/tour.png
--------------------------------------------------------------------------------
/img/tour2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/tour2.png
--------------------------------------------------------------------------------
/img/train.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/train.png
--------------------------------------------------------------------------------
/img/banque.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/banque.png
--------------------------------------------------------------------------------
/img/banque2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/banque2.png
--------------------------------------------------------------------------------
/img/house2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/house2.png
--------------------------------------------------------------------------------
/img/light2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/light2.png
--------------------------------------------------------------------------------
/img/maison.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/maison.png
--------------------------------------------------------------------------------
/img/money2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/money2.png
--------------------------------------------------------------------------------
/img/prison.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/prison.png
--------------------------------------------------------------------------------
/old/plateau.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/old/plateau.jpg
--------------------------------------------------------------------------------
/img/behaviour.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/behaviour.png
--------------------------------------------------------------------------------
/img/big_train.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/big_train.png
--------------------------------------------------------------------------------
/img/info-user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/info-user.png
--------------------------------------------------------------------------------
/img/info-user2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/info-user2.png
--------------------------------------------------------------------------------
/img/strategy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/strategy.png
--------------------------------------------------------------------------------
/img/little_train.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/little_train.png
--------------------------------------------------------------------------------
/img/mr_monopoly.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/mr_monopoly.jpg
--------------------------------------------------------------------------------
/img/mr_monopoly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/mr_monopoly.png
--------------------------------------------------------------------------------
/img/reload_dice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/reload_dice.png
--------------------------------------------------------------------------------
/img/interrogation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/interrogation.png
--------------------------------------------------------------------------------
/img/jamesbond/woman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/jamesbond/woman.png
--------------------------------------------------------------------------------
/img/starwars/r2_d2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/r2_d2.png
--------------------------------------------------------------------------------
/img/starwars/xwing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/xwing.png
--------------------------------------------------------------------------------
/old/monopoly_old.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/old/monopoly_old.html
--------------------------------------------------------------------------------
/img/jamesbond/gadget.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/jamesbond/gadget.png
--------------------------------------------------------------------------------
/img/jamesbond/spectre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/jamesbond/spectre.png
--------------------------------------------------------------------------------
/img/starwars/clone_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/clone_1.png
--------------------------------------------------------------------------------
/img/starwars/clone_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/clone_2.png
--------------------------------------------------------------------------------
/img/starwars/clone_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/clone_3.png
--------------------------------------------------------------------------------
/img/starwars/clone_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/clone_4.png
--------------------------------------------------------------------------------
/img/starwars/sw_c3po.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/sw_c3po.png
--------------------------------------------------------------------------------
/img/starwars/sw_clone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/sw_clone.png
--------------------------------------------------------------------------------
/img/starwars/sw_r2d2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/sw_r2d2.png
--------------------------------------------------------------------------------
/img/starwars/boba_fett.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/boba_fett.png
--------------------------------------------------------------------------------
/img/starwars/darth_vader.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/darth_vader.png
--------------------------------------------------------------------------------
/img/starwars/jango_fett.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/jango_fett.png
--------------------------------------------------------------------------------
/poc/img_test/icon_ok_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/poc/img_test/icon_ok_1.png
--------------------------------------------------------------------------------
/poc/img_test/icon_ok_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/poc/img_test/icon_ok_10.png
--------------------------------------------------------------------------------
/poc/img_test/icon_ok_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/poc/img_test/icon_ok_2.png
--------------------------------------------------------------------------------
/poc/img_test/icon_ok_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/poc/img_test/icon_ok_3.png
--------------------------------------------------------------------------------
/poc/img_test/icon_ok_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/poc/img_test/icon_ok_4.png
--------------------------------------------------------------------------------
/poc/img_test/icon_ok_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/poc/img_test/icon_ok_5.png
--------------------------------------------------------------------------------
/poc/img_test/icon_ok_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/poc/img_test/icon_ok_6.png
--------------------------------------------------------------------------------
/poc/img_test/icon_ok_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/poc/img_test/icon_ok_7.png
--------------------------------------------------------------------------------
/poc/img_test/icon_ok_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/poc/img_test/icon_ok_8.png
--------------------------------------------------------------------------------
/poc/img_test/icon_ok_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/poc/img_test/icon_ok_9.png
--------------------------------------------------------------------------------
/img/jamesbond/aston_martin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/jamesbond/aston_martin.png
--------------------------------------------------------------------------------
/img/starwars/stormtrooper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/stormtrooper.png
--------------------------------------------------------------------------------
/img/starwars/sw_boba_fett.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/sw_boba_fett.png
--------------------------------------------------------------------------------
/img/starwars/sw_chewbacca.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/sw_chewbacca.png
--------------------------------------------------------------------------------
/img/starwars/jedi_light_saber.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/jedi_light_saber.png
--------------------------------------------------------------------------------
/img/starwars/sith_light_saber.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/sith_light_saber.png
--------------------------------------------------------------------------------
/img/starwars/sw_darth_vader.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/starwars/sw_darth_vader.png
--------------------------------------------------------------------------------
/img/jamesbond/universal_export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jotitan/monopoly/HEAD/img/jamesbond/universal_export.png
--------------------------------------------------------------------------------
/data/data-monopoly-circle-bond.json:
--------------------------------------------------------------------------------
1 | {
2 | "extend":"data-monopoly-jamesbond.json",
3 | "plateau":{
4 | "subtitle":"Round",
5 | "type":"circle"
6 |
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/data/data-monopoly-couleur.json:
--------------------------------------------------------------------------------
1 | {
2 | "extend":"data-monopoly.json",
3 | "plateau":{
4 | "nomsJoueurs":["Bleu","Rouge","Orange","Vert","Gris","Marron","Violet"]
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/data/data-monopoly-circle-starwars2.json:
--------------------------------------------------------------------------------
1 | {
2 | "extend":"data-monopoly-starwars2.json",
3 | "plateau":{
4 | "type":"circle"
5 | },
6 | "images":{
7 | "default":{
8 | "marginTop":140,
9 | "marginLeft":0.25,
10 | "rotate":-90
11 | }
12 | }
13 |
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/dice/throw_dice.go:
--------------------------------------------------------------------------------
1 | package dice
2 |
3 | import (
4 | "math/rand"
5 | "time"
6 | )
7 |
8 | var randSource = rand.New(rand.NewSource(time.Now().UnixNano()))
9 |
10 | func Throw(nbDice int)[]int{
11 | dices := make([]int,nbDice)
12 | for i := 0 ; i < nbDice ; i++{
13 | dices[i] = randSource.Int()%6+1
14 | }
15 | return dices
16 | }
17 |
--------------------------------------------------------------------------------
/poc/pi.js:
--------------------------------------------------------------------------------
1 | // Calcul de PI
2 |
3 | var NB_LANCER = 200000;
4 | var results = {in:0,out:0};
5 | for(var i = 0 ; i < NB_LANCER ; i++){
6 | var x = rand();
7 | var y = rand();
8 | if(x*x + y*y <=1){results.in++;}
9 | else{results.out++;}
10 | }
11 | var pi = (results.in/NB_LANCER)*4;
12 | console.log(pi);
13 |
14 | function rand(){
15 | return (Math.random()*1000000000)/1000000000;
16 | }
--------------------------------------------------------------------------------
/data/data-monopoly-starwars2.json:
--------------------------------------------------------------------------------
1 | {
2 | "extend":"data-monopoly-starwars.json",
3 | "plateau":{
4 | "subtitle":"Star Wars Personnage",
5 | "nomsJoueurs":["R2 D2","C3 PO","Dark Vador","StormTrooper","Boba Feet","Chewbacca"],
6 | "imgJoueurs":["img/starwars/sw_r2d2.png","img/starwars/sw_c3po.png","img/starwars/sw_darth_vader.png","img/starwars/sw_clone.png","img/starwars/sw_boba_fett.png","img/starwars/sw_chewbacca.png"]
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/data/data-monopoly-circle.json:
--------------------------------------------------------------------------------
1 | {
2 | "extend":"data-monopoly.json",
3 | "plateau":{
4 | "subtitle":"Round",
5 | "type":"circle"
6 | },
7 | "images":{
8 | "default":{
9 | "marginTop":150,
10 | "marginLeft":0.25,
11 | "rotate":-90
12 | },
13 | "gare":{
14 | "width":40,
15 | "height":20
16 | },
17 | "chance":{
18 | "width":25,
19 | "height":30
20 | },
21 | "caisseDeCommunaute":{
22 | "height":30,
23 | "width":25,
24 | "marginLeft":0.15
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/js/request_service.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | function get(path){
4 | return fetch(path).then(d=>d.json())
5 | }
6 |
7 | function post(path, data=''){
8 | return fetch(path,{method:'POST',body:data}).then(d=>d.json())
9 | }
10 |
11 | function createGameRequest(){
12 | return post('/createGame')
13 | }
14 |
15 | function loadAllPlateaux(){
16 | return get('data/plateaux.json');
17 | }
18 |
19 | function dices(nb){
20 | return get(`dices?nb=${nb}`);
21 | }
22 |
23 | function sendEventOnNetwork(url, event){
24 | return post(url, JSON.stringify(event))
25 | }
26 |
27 | export {get, post, createGameRequest, loadAllPlateaux, dices, sendEventOnNetwork};
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "copy-webpack-plugin": "^11.0.0",
4 | "css-loader": "^6.8.1",
5 | "grunt": "^1.6.1",
6 | "grunt-cli": "^1.4.3",
7 | "grunt-contrib-clean": "^2.0.1",
8 | "grunt-contrib-copy": "^1.0.0",
9 | "grunt-contrib-uglify": "^5.2.2",
10 | "grunt-contrib-watch": "^1.1.0",
11 | "grunt-targethtml": "^0.2.6",
12 | "grunt-zip": "^0.20.0",
13 | "html-replace-webpack-plugin": "^2.6.0",
14 | "html-webpack-plugin": "^5.5.3",
15 | "string-replace-loader": "^3.1.0",
16 | "webpack": "^5.88.2",
17 | "webpack-cli": "^5.1.4",
18 | "zip-webpack-plugin": "^4.0.1"
19 | },
20 | "scripts": {
21 | "pack": "webpack --config ./webpack-conf.js",
22 | "pack-github": "webpack --config ./webpack-conf-public.js"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/js/bus_message.js:
--------------------------------------------------------------------------------
1 |
2 | // Manage messages between parts of app
3 |
4 | class Bus {
5 | constructor(){
6 | this.observers = {};
7 | }
8 | network(data){
9 | this.send('event.network',data);
10 | }
11 | debug(data){
12 | this.send('monopoly.debug',data);
13 | }
14 |
15 | refresh(){
16 | this.send('refreshPlateau');
17 | }
18 |
19 | send(key, data = {}){
20 | const clients = this.observers[key];
21 | if(clients != null){
22 | clients.forEach(callback => callback(data))
23 | }
24 | }
25 | observe(key, callback){
26 | let clients = this.observers[key];
27 | if(clients == null) {
28 | clients = [];
29 | this.observers[key] = clients;
30 | }
31 | clients.push(callback);
32 | }
33 |
34 | watchRefresh(callback) {
35 | this.observe('refreshPlateau',callback);
36 | }
37 | }
38 |
39 | const bus = new Bus()
40 |
41 | export {bus}
--------------------------------------------------------------------------------
/data/plateaux.json:
--------------------------------------------------------------------------------
1 | {
2 | "comment": "Remplace le futur service listant dynamiquement les fichiers de configuration",
3 | "plateaux": [
4 | {
5 | "name": "Classique",
6 | "url": "data-monopoly.json"
7 | },
8 | {
9 | "name": "Original",
10 | "url": "data-monopoly-original.json"
11 | },
12 | {
13 | "name": "Classique Circle",
14 | "url": "data-monopoly-circle.json"
15 | },
16 | {
17 | "name": "Couleur",
18 | "url": "data-monopoly-couleur.json"
19 | },
20 | {
21 | "name": "James Bond",
22 | "url": "data-monopoly-jamesbond.json"
23 | },
24 | {
25 | "name": "James Bond Circle",
26 | "url": "data-monopoly-circle-bond.json"
27 | },
28 | {
29 | "name": "Star Wars",
30 | "url": "data-monopoly-starwars.json"
31 | },
32 | {
33 | "name": "Star Wars 2",
34 | "url": "data-monopoly-starwars2.json"
35 | },
36 | {
37 | "name": "Star Wars 2 Circle",
38 | "url": "data-monopoly-circle-starwars2.json"
39 | }
40 | ,
41 | {
42 | "name": "Monopoly Junior",
43 | "url": "data-monopoly-junior.json"
44 | }
45 | ]
46 | }
--------------------------------------------------------------------------------
/webpack-conf.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 | const HtmlReplaceWebpackPlugin = require('html-replace-webpack-plugin');
3 | const CopyPlugin = require('copy-webpack-plugin');
4 | const ZipPlugin = require('zip-webpack-plugin');
5 |
6 | const webpackConf = require('webpack'); //to access built-in plugins
7 |
8 | const path = require('path');
9 |
10 | module.exports = {
11 | entry: './js/core/monopoly.js',
12 | mode: 'production',
13 | output: {
14 | path: path.join(__dirname, 'dist'),
15 | filename: 'my-bundle.js'
16 | },
17 | module: {
18 | rules: [
19 | {
20 | test: /monopoly\.js$/,
21 | loader: 'string-replace-loader',
22 | options: {
23 | search: /let debug = true;/i,
24 | replace: 'let debug = false;'
25 | }
26 | }
27 | ]
28 | },
29 | plugins: [
30 | new HtmlWebpackPlugin({template: './monopoly-template.html'}),
31 | new HtmlReplaceWebpackPlugin([
32 | {
33 | pattern: '',
34 | replacement: ''
35 | }]),
36 | new CopyPlugin({
37 | patterns: [
38 | {from: "css", to: "css"},
39 | {from: "img", to: "img"},
40 | {from: "lib", to: "lib"},
41 | {from: "data", to: "data"},
42 | ],
43 | }),
44 | new ZipPlugin({
45 | path: '',
46 | filename: 'monopoly_build.zip',
47 | })
48 | ]
49 |
50 | };
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # Workflow to build at each commit in master the application
2 |
3 | name: CI build Monopoly
4 |
5 | # Controls when the action will run. Triggers the workflow on push or pull request
6 | # events but only for the master branch
7 | on:
8 | push:
9 | branches: [ master ]
10 | pull_request:
11 | branches: [ master ]
12 |
13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
14 | jobs:
15 | # This workflow contains a single job called "build"
16 | build:
17 | # The type of runner that the job will run on
18 | runs-on: ubuntu-latest
19 |
20 | # Steps represent a sequence of tasks that will be executed as part of the job
21 | steps:
22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
23 | - uses: actions/checkout@v3
24 |
25 | - name: Build go server
26 | run: |
27 | echo Run build server go
28 | cd src/main
29 | export GOOS=linux
30 | export GOARCH=arm
31 | go build -o run_monopoly run_monopoly.go
32 | cd ../../
33 | mkdir artifacts
34 | echo Copy monopoly server to artifacts/
35 | mv src/main/run_monopoly artifacts
36 |
37 | - uses: actions/setup-node@v3
38 | with:
39 | node-version: '16'
40 |
41 | - name: Build front
42 | run: |
43 | echo Install and build app,
44 | npm install
45 | npm run pack
46 | mv dist/monopoly_build.zip artifacts
47 |
48 | # Upload artifact
49 | - uses: actions/upload-artifact@v4
50 | with:
51 | name: Artifacts monopoly
52 | path: artifacts
53 |
--------------------------------------------------------------------------------
/README.textile:
--------------------------------------------------------------------------------
1 | h1. Monopoly full JS !https://codebeat.co/badges/843f9fbf-e914-495f-8d9f-2f85343cc22b(codebeat badge)!:https://codebeat.co/projects/github-com-jotitan-monopoly-master !https://github.com/jotitan/monopoly/actions/workflows/main.yml/badge.svg?branch=master!
2 |
3 | Implementation Full Javascript du Monopoly.
4 | Aucune librairie Javascript et packaging avec Webpack.
5 |
6 | h2. Démo
7 |
8 | Demo du monopoly : "Monopoly":http://jotitan.github.io/monopoly/demo/
9 |
10 | h2. Fonctionnalités
11 |
12 | * Possibilité de jouer à 6 sur le même ordinateur
13 | * IA : plusieurs robots peuvent jouer. Différents niveaux sont implémentés (gestion de l'argent, choix des terrains et agressivité).
14 | * Plusieurs variantes : case départ x 2, parc gratuit, enchere immédiate
15 | * Possibilité d'échanger les terrains avec les autres joueurs
16 | * Utilisation du dé rapide (règle récente)
17 | * Plusieurs plateaux sont disponibles : classique, en cercle
18 | * Possibilité de customiser les plateaux (images, nom des joueurs, nom des rues, cartes, couleurs)
19 | * Sauvegarde la partie (en cliquant sur "MONOPOLY")
20 | * Gestion du réseau (serveur en Go)
21 |
22 | h2. Build
23 |
24 | Il est possible de tester l'application localement en ouvrant monopoly-template.html
25 | Vous pouvez également l'application pour réduire sa taille en lançant :
26 | ```shell
27 | npm run pack
28 | ```
29 |
30 | h2. Capture de la version avec dé rapide :
31 |
32 | !https://raw.github.com/jotitan/monopoly-js/gh-pages/screenshots/screenshot2.png!
33 |
34 | h2. Version en cercle du Monopoly
35 |
36 | !https://raw.github.com/jotitan/monopoly-js/gh-pages/screenshots/screenshot-circle.png!
37 |
38 | h2. Todo
39 |
40 | * Accepter plus facilement les propositions vers la fin du jeu, surtout si on a rien
41 |
--------------------------------------------------------------------------------
/webpack-conf-public.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 | const HtmlReplaceWebpackPlugin = require('html-replace-webpack-plugin');
3 | const CopyPlugin = require('copy-webpack-plugin');
4 | const ZipPlugin = require('zip-webpack-plugin');
5 |
6 | const webpackConf = require('webpack'); //to access built-in plugins
7 |
8 | const path = require('path');
9 |
10 | module.exports = {
11 | entry: './js/core/monopoly.js',
12 | mode: 'production',
13 | output: {
14 | path: path.join(__dirname, 'dist'),
15 | filename: 'my-bundle.js'
16 | },
17 | module: {
18 | rules: [
19 | {
20 | test: /monopoly\.js$/,
21 | loader: 'string-replace-loader',
22 | options: {
23 | multiple:[{
24 | search: /let enableNetwork = true;/i,
25 | replace: 'let enableNetwork = false;'
26 | }, {
27 | search: /let debug = true;/i,
28 | replace: 'let debug = false;'
29 | }]
30 | }
31 | }
32 | ]
33 | },
34 | plugins: [
35 | new HtmlWebpackPlugin({template: './monopoly-template.html'}),
36 | new HtmlReplaceWebpackPlugin([
37 | {
38 | pattern: '',
39 | replacement: ''
40 | },{
41 | pattern: 'let debug = true;',
42 | replacement: 'let debug = false;'
43 | },{
44 | pattern: 'let enableNetwork = true;',
45 | replacement: 'let enableNetwork = false;'
46 | }]),
47 |
48 | new CopyPlugin({
49 | patterns: [
50 | {from: "css", to: "css"},
51 | {from: "img", to: "img"},
52 | {from: "lib", to: "lib"},
53 | {from: "data", to: "data"},
54 | ],
55 | })
56 | ]
57 |
58 | };
--------------------------------------------------------------------------------
/poc/test-eyes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Test
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/js/utils.js:
--------------------------------------------------------------------------------
1 | /* Fonctions utilitaires */
2 |
3 | // Defini la methode size. Cette methode evite d'etre enumere dans les boucles
4 | Object.defineProperty(Array.prototype, "size", {
5 | value: function () {
6 | let count = 0;
7 | for (let i in this) {
8 | count++;
9 | }
10 | return count;
11 | },
12 | writable: false,
13 | enumerable: false,
14 | configurable: false
15 | });
16 |
17 | Object.defineProperty(Array.prototype, "contains", {
18 | value: function (value) {
19 | for (let i in this) {
20 | if (this[i] === value) {
21 | return true;
22 | }
23 | }
24 | return false;
25 | },
26 | writable: false,
27 | enumerable: false,
28 | configurable: false
29 | });
30 |
31 | Object.defineProperty(Array.prototype, "filter", {
32 | value: function (callback) {
33 | const list = [];
34 | for (const i in this) {
35 | if (callback(this[i])) {
36 | list.push(this[i]);
37 | }
38 | }
39 | return list;
40 | },
41 | writable: false,
42 | enumerable: false,
43 | configurable: false
44 | });
45 |
46 | Object.defineProperty(Array.prototype, "some", {
47 | value: function (callback) {
48 | for (let i in this) {
49 | if (callback(this[i])) {
50 | return true;
51 | }
52 | }
53 | return false;
54 | },
55 | writable: false,
56 | enumerable: false,
57 | configurable: false
58 | });
59 |
60 | function deepCopy(a,b){
61 | let copy = {...a};
62 | Object.keys(b).forEach(k=>{
63 | if(copy[k] == null){
64 | copy[k] = b[k];
65 | }else{
66 | if(typeof b[k] === 'object' && !Array.isArray(b[k])){
67 | copy[k] = deepCopy(copy[k],b[k]);
68 | }else{
69 | copy[k] = b[k];
70 | }
71 | }
72 | })
73 | return copy;
74 | }
75 |
76 | function disableActions(){
77 | document.querySelectorAll('.action-joueur').forEach(d=>{
78 | d.classList.add('disabled');
79 | d.setAttribute('disabled','disabled');
80 | })
81 | }
82 |
83 | function enableActions(){
84 | document.querySelectorAll('.action-joueur').forEach(d=>{
85 | d.classList.remove('disabled');
86 | d.removeAttribute('disabled');
87 | })
88 | }
89 |
90 | export {deepCopy, disableActions, enableActions}
--------------------------------------------------------------------------------
/js/entity/network/local_joueur.js:
--------------------------------------------------------------------------------
1 | import {NetworkJoueur, Joueur, Notifier} from '../joueur.js';
2 | import {GestionFiche} from "../../display/case_jeu.js";
3 | import {GestionDes} from "../dices.js";
4 | import {GestionJoueur} from "../../core/gestion_joueurs.js";
5 |
6 | /* Represent a local player in remote game */
7 | /* Extend joueur but override many methods, no action, only receive events */
8 | class LocalPlayer extends NetworkJoueur {
9 | constructor(numero, nom, color,argent,montantDepart){
10 | super(numero,nom,color,argent,montantDepart);
11 | }
12 | lancerDes() {
13 | // Send event to get score for dices. If prison, ask stay or not before
14 | GestionDes.gestionDes.before(()=>Notifier.askDices(this));
15 | }
16 | notifySelect(){}
17 | // Notify to master end of turn
18 | endTurn(){
19 | Notifier.notifyEnd()
20 | }
21 | notifyDices(dices){}
22 | }
23 |
24 | class RemotePlayer extends Joueur{
25 | constructor(numero,nom,color,argent,montantDepart){
26 | super(numero,nom,color,argent,montantDepart);
27 | this.canPlay = false;
28 | this.type = "Distant";
29 | this.free = true;
30 | }
31 | isSlotFree(){
32 | return this.free;
33 | }
34 | setPlayer(player){
35 | this.nom = player;
36 | this.free = false;
37 | this.div.querySelector('.joueur-name').innerHTML = this.nom;
38 | }
39 | // If remote player receive money, it's done and send to all
40 | notifyPay(montant){
41 | Notifier.payer(montant,this);
42 | }
43 | }
44 |
45 | // Remote manage by master, must send his events
46 | class MasterRemotePlayer extends RemotePlayer{
47 | constructor(numero,nom,color,argent,montantDepart) {
48 | super(numero, nom, color, argent,montantDepart);
49 | }
50 | // Send event when set as player
51 | notifySelect(){
52 | Notifier.notifySelect(this);
53 | }
54 | notifyDices(dices,event){
55 | // Send event tax if necessary, move to and change
56 | Notifier.dices(dices,event,this);
57 | if(event.prison != null) {
58 | if (!event.prison.sortie) {
59 | GestionJoueur.change()
60 | }else{
61 | GestionJoueur.getJoueurCourant().exitPrison()
62 | // exist but must by or relaunch
63 | }
64 | }
65 | }
66 | moveTo(nb){
67 | let nextCase = this.pion.deplaceValeursDes(nb);
68 | Notifier.moveTo(GestionFiche.buildId(nextCase),this);
69 | this.joueSurCase(nextCase);
70 | }
71 | }
72 |
73 | export {MasterRemotePlayer,RemotePlayer,LocalPlayer};
74 |
--------------------------------------------------------------------------------
/poc/jquery.mousewheel.min.js:
--------------------------------------------------------------------------------
1 | /*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
2 | * Licensed under the MIT License (LICENSE.txt).
3 | *
4 | * Version: 3.1.12
5 | *
6 | * Requires: jQuery 1.2.2+
7 | */
8 | !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});
--------------------------------------------------------------------------------
/js/entity/pion.js:
--------------------------------------------------------------------------------
1 | /* Objet PION */
2 | import {DrawerFactory,Drawer} from "../ui/graphics.js";
3 | import {GestionJoueur} from "../core/gestion_joueurs.js";
4 | import {VARIANTES} from "../core/monopoly.js";
5 | import {bus} from "../bus_message.js";
6 |
7 | function Pion(color, joueur,img, montantDepart = 20000) {
8 | this.axe = 2;
9 | this.position = 0;
10 | this.joueur = joueur;
11 | this.montantDepart = montantDepart;
12 | this.stats = {
13 | tour: 0,
14 | prison: 0,
15 | positions:[]
16 | }; // stat du joueur
17 | this.pion = DrawerFactory.getPionJoueur(color,DrawerFactory.dimensions.largeurPion,img,this.joueur);
18 | Drawer.addRealTime(this.pion);
19 |
20 | /* Supprime le pion en cas de defaite */
21 | this.remove = function () {
22 | Drawer.removeComponent(this.pion);
23 | }
24 |
25 | // Ca directement en prison, sans passer par la case depart, en coupant
26 | this.goPrison = function (callback) {
27 | this.stats.prison++;
28 | // Manque le callback
29 | this.goDirectToCell(3, 0, callback);
30 | }
31 |
32 | this.deplaceValeursDes = function (des) {
33 | let pos = this.position + des;
34 | let axe = this.axe;
35 | while(pos<0){
36 | pos+=DrawerFactory.dimensions.nbCases;
37 | axe= (axe + 3)%4;
38 | }
39 | while (pos >= DrawerFactory.dimensions.nbCases) {
40 | pos -= DrawerFactory.dimensions.nbCases;
41 | axe = (axe + 1) % 4;
42 | }
43 | return {
44 | pos: pos,
45 | axe: axe
46 | }
47 | };
48 |
49 | this.goto = function (axe, pos, call,direct=VARIANTES.quickMove,primeDepart=true) {
50 | let id = axe+"-"+pos;
51 |
52 | if(this.stats.positions[id] == null){
53 | this.stats.positions[id] = 1;
54 | }
55 | else{
56 | this.stats.positions[id]++;
57 | }
58 | if(GestionJoueur.getJoueurCourant() != null) {
59 | bus.debug( {
60 | message: `${GestionJoueur.getJoueurCourant().nom} est en ${this.axe} - ${this.position} et va en ${id}`
61 | });
62 | }
63 | // On gere le cas de la case depart (si elle est sur le trajet)
64 | let depart = this.axe*DrawerFactory.dimensions.nbCases + this.position;
65 | let cible = axe*DrawerFactory.dimensions.nbCases + pos;
66 | let caseDepart = 2*DrawerFactory.dimensions.nbCases;
67 | if(primeDepart && ((depart < caseDepart && cible > caseDepart) || (depart > cible && (depart < caseDepart || cible > caseDepart)))){
68 | this.treatCaseDepart();
69 | }
70 | this.axe = axe;
71 | this.position = pos;
72 | this.pion.goto(axe, pos, call,false,direct);
73 | };
74 |
75 | // Si on passe par la case depart, on prend 20000 Francs
76 | this.treatCaseDepart = function () {
77 | this.stats.tour++;
78 | this.joueur.gagner(this.montantDepart);
79 | }
80 |
81 | this.goDirectToCell = function (axe, pos, callback) {
82 | this.axe = axe;
83 | this.position = pos;
84 | this.pion.gotoDirect(axe,pos,callback);
85 | }
86 | }
87 |
88 | export {Pion};
89 |
--------------------------------------------------------------------------------
/js/sauvegarde.js:
--------------------------------------------------------------------------------
1 | /* Gestion de la sauvegarde */
2 | import {GestionJoueur} from "./core/gestion_joueurs.js";
3 | import {GestionFiche} from "./display/case_jeu.js";
4 | import {VARIANTES, globalStats, updateVariantes} from "./core/monopoly.js";
5 | import {bus} from "./bus_message.js";
6 | import {deepCopy} from "./utils.js";
7 |
8 | let Sauvegarde = {
9 | prefix: "monopoly.",
10 | suffix: ".save",
11 | currentSauvegardeName:null,
12 | isSauvegarde:function(){
13 | return this.currentSauvegardeName!=null;
14 | },
15 | save: function (name, plateau) {
16 | this.currentSauvegardeName = name !=null ? this.getSauvegardeName(name) : this.currentSauvegardeName || this.getSauvegardeName();
17 | this.saveWithName(this.currentSauvegardeName,plateau);
18 | bus.send('monopoly.save',{name: this.currentSauvegardeName});
19 | },
20 | saveWithName: function (saveName, plateau) {
21 | // On recupere la liste des joueurs
22 | let saveJoueurs = [];
23 | GestionJoueur.joueurs.filter(j=>j.saver).forEach(j=>saveJoueurs.push(j.saver.save()));
24 | // On recupere la liste des fiches
25 | let saveFiches = [];
26 | let it = GestionFiche.iteratorTerrains();
27 | while (it.hasNext()) {
28 | saveFiches.push(it.next().save());
29 | }
30 | let data = {
31 | joueurs: saveJoueurs,
32 | fiches: saveFiches,
33 | joueurCourant: GestionJoueur.getJoueurCourant() != null ? GestionJoueur.getJoueurCourant().id:'',
34 | variantes: VARIANTES,
35 | options:plateau.options,
36 | nbTours: globalStats.nbTours,
37 | plateau:plateau.name
38 | };
39 | this._putStorage(saveName, data);
40 | },
41 | load: function (name, monopoly) {
42 | this.currentSauvegardeName = name;
43 | let data = this._getStorage(name);
44 | // On charge le plateau
45 | updateVariantes(deepCopy(VARIANTES, data.variantes));
46 | //VARIANTES = data.variantes || VARIANTES;
47 | monopoly.plateau.load(data.plateau || "data-monopoly.json",data.options,function(){
48 | data.joueurs.forEach((j,i)=>GestionJoueur.createAndLoad(!j.canPlay, i,j.nom,j,monopoly.plateau.infos.montantDepart));
49 | data.fiches.forEach(f=>GestionFiche.getById(f.id).load(f));
50 | bus.refresh();
51 | globalStats.nbTours = data.nbTours || 0;
52 | monopoly.afterCreateGame();
53 | GestionJoueur.change(data.joueurCourant);
54 | });
55 | },
56 | delete: function (name) {
57 | localStorage.removeItem(name);
58 | },
59 | _putStorage: function (name, data) {
60 | localStorage[name] = JSON.stringify(data);
61 | },
62 | _getStorage: function (name) {
63 | if (localStorage[name] == null) {
64 | throw "Aucune sauvegarde";
65 | }
66 | const data = localStorage[name];
67 | return JSON.parse(data);
68 | },
69 | autoSave: function () {
70 |
71 | },
72 | findSauvegardes: function () {
73 | let exp = "^" + this.prefix + "(.*)" + this.suffix + "$";
74 | let list = [];
75 | for (let name in localStorage) {
76 | let label = new RegExp(exp, "g").exec(name);
77 | if (label != null) {
78 | list.push({
79 | value: name,
80 | label: label[1]
81 | });
82 | }
83 | }
84 | return list;
85 | },
86 | getSauvegardeName: function (name) {
87 | return this.prefix + ((name == null || name === "") ? new Date().getTime() : name) + this.suffix;
88 | }
89 | };
90 |
91 |
92 |
93 | export {Sauvegarde};
94 |
--------------------------------------------------------------------------------
/js/entity/cartes_action.js:
--------------------------------------------------------------------------------
1 | /* Cartes actions (chance ou caisse de communaute) */
2 |
3 | import {GestionJoueur} from "../core/gestion_joueurs.js";
4 |
5 | class CarteAction{
6 | constructor(type) {
7 | this.type = type;
8 | }
9 | action(joueur) {
10 | console.error("Not implemented " + joueur,this.type);
11 | }
12 | }
13 |
14 | /* Carte chance : reparations des maisons et hotels */
15 | class ReparationsCarte extends CarteAction{
16 | constructor(tarifHotel,tarifMaison) {
17 | super("reparation");
18 | this.tarifHotel = tarifHotel;
19 | this.tarifMaison = tarifMaison;
20 | }
21 | action(joueur){
22 | let stats = joueur.getStats();
23 | let montant = stats.hotel * this.tarifHotel + stats.maison * this.tarifMaison;
24 | joueur.payer(montant,()=>GestionJoueur.change());
25 | }
26 | }
27 |
28 | /* Carte chance : chaque joueur vous donne 1000 francs */
29 | class BirthdayCarte extends CarteAction{
30 | constructor(montant){
31 | super("birthday");
32 | this.montant = montant;
33 | }
34 | action(joueur){
35 | let payers = GestionJoueur.joueurs.filter(j=>!joueur.equals(j) && !j.defaite);
36 | payers.forEach((j,i)=>{
37 | j.payerTo(this.montant,joueur,()=>{
38 | if(i === payers.length -1){
39 | GestionJoueur.change();
40 | }
41 | });
42 | });
43 | }
44 | }
45 |
46 | /* Action de déplacement vers une case */
47 | /* @param direct : si renseigné a vrai, le pion est deplacé directement vers la case, sans passer par la case depart */
48 | class GotoCarte extends CarteAction{
49 | constructor(axe, pos, direct,primeDepart=true) {
50 | super("goto");
51 | this.fiche = {axe:axe,pos:pos};
52 | this.direct = direct;
53 | this.primeDepart = primeDepart;
54 | }
55 | action(joueur) {
56 | joueur.joueSurCase(this.fiche,this.direct,this.primeDepart, true);
57 | }
58 | }
59 |
60 | /* Carte sortie de prison */
61 | class PrisonCarte extends CarteAction {
62 | constructor() {
63 | super("prison");
64 | this.joueurPossede = null;
65 | }
66 | action(joueur) {
67 | joueur.cartesSortiePrison.push(this);
68 | this.joueurPossede = joueur;
69 | GestionJoueur.change();
70 | }
71 | isLibre () {
72 | return this.joueurPossede == null;
73 | }
74 | }
75 |
76 | /* Action de déplacement d'un certain nombre de case */
77 | class MoveNbCarte extends CarteAction {
78 | constructor(nb,direct=true,primeDepart=true) {
79 | super("move");
80 | this.nb = nb;
81 | this.direct = direct;
82 | // If false, no prime on start case
83 | this.primeDepart = primeDepart;
84 | }
85 | action(joueur) {
86 | let pos = joueur.pion.deplaceValeursDes(this.nb);
87 | joueur.joueSurCase(pos,this.direct,this.primeDepart);
88 | }
89 | }
90 |
91 | /* Action de gain d'argent pour une carte */
92 | class PayerCarte extends CarteAction {
93 | constructor(montant,plateauMonopoly) {
94 | super("taxe");
95 | this.plateauMonopoly = plateauMonopoly;
96 | this.montant = montant;
97 | }
98 | action(joueur) {
99 | joueur.payerParcGratuit(this.plateauMonopoly.parcGratuit,this.montant, ()=>GestionJoueur.change());
100 | }
101 | }
102 |
103 | /* Action de perte d'argent pour une carte */
104 | class GagnerCarte extends CarteAction {
105 | constructor(montant) {
106 | super("prime");
107 | this.montant = montant;
108 | }
109 | action(joueur){
110 | joueur.gagner(this.montant);
111 | GestionJoueur.change();
112 | }
113 | }
114 |
115 | let CarteActionFactory = {
116 | get:function(data, plateauMonopoly) {
117 | switch (data.type) {
118 | /* Amende a payer */
119 | case "taxe":
120 | return new PayerCarte(data.montant,plateauMonopoly);
121 | /* Argent a toucher */
122 | case "prime":
123 | return new GagnerCarte(data.montant);
124 | /* Endroit ou aller */
125 | case "goto":
126 | return new GotoCarte(data.axe, data.pos, data.direct,data.primeDepart);
127 | /* Deplacement a effectuer */
128 | case "move":
129 | return new MoveNbCarte(data.nb,data.direct,data.primeDepart);
130 | /* Carte prison */
131 | case "prison":
132 | return new PrisonCarte();
133 | /* Carte anniversaire */
134 | case "birthday":
135 | return new BirthdayCarte(data.montant);
136 | case "repair":
137 | return new ReparationsCarte(data.hotel,data.maison)
138 | }
139 | throw "Type inconnu";
140 | }
141 | };
142 |
143 | export {CarteActionFactory};
144 |
145 |
--------------------------------------------------------------------------------
/data/data-monopoly-junior.json:
--------------------------------------------------------------------------------
1 | {
2 | "plateau":{
3 | "subtitle":"Classique",
4 | "backgroundColor": "#cee6d0",
5 | "textColor": "#000000",
6 | "nomsJoueurs":["Gluglu","Yoyo","Bibou","Sorceline"],
7 | "colors":["#319c39","#3a56a4","#9f3338","#cfd4d0"],
8 | "nbCases": 6,
9 | "argent": 31,
10 | "depart": 2,
11 | "prison": 1,
12 | "hideConstructions": true
13 | },
14 | "background-image":"",
15 | "currency": "M",
16 | "fiches": [
17 | {"type": "depart","axe": 2,"pos": 0,"nom": "Départ"},
18 | {"type": "propriete-junior","groupe": "Violet","axe": 2,"pos": 1,"nom": "Bus de Ninjaka","colors": ["#964526", "#cca494"],"prix": 1},
19 | {"type": "propriete-junior","groupe": "Violet","axe": 2,"pos": 2,"nom": "Ninjaka","colors": ["#964526", "#cca494"],"prix": 1},
20 | {"type": "chance","axe": 2,"pos": 3},
21 | {"type": "propriete-junior","groupe": "Bleu clair","axe": 2,"pos": 4,"nom": "Labo","colors": ["#bbeefe", "#d2f1fb"],"prix": 1},
22 | {"type": "propriete-junior","groupe": "Bleu clair","axe": 2,"pos": 5,"nom": "Roméo","colors": ["#bbeefe", "#d2f1fb"],"prix": 1},
23 | {"type": "special","axe": 3,"pos": 0,"nom": "Simple visite"},
24 | {"type": "propriete-junior","groupe": "Violet clair","axe": 3,"pos": 1,"nom": "Aéro-lune","colors": ["#e0338a", "#df83b1"],"prix": 2},
25 | {"type": "propriete-junior","groupe": "Violet clair","axe": 3,"pos": 2,"nom": "Sorceline","colors": ["#e0338a", "#df83b1"],"prix": 2},
26 | {"type": "chance","axe": 3,"pos": 3},
27 | {"type": "propriete-junior","groupe": "Orange","axe": 3,"pos": 4,"nom": "Pyja-robot","colors": ["#ff9500", "#ffb854"],"prix": 2},
28 | {"type": "propriete-junior","groupe": "Orange","axe": 3,"pos": 5,"nom": "Ninjazouaves","colors": ["#ff9500", "#ffb854"],"prix": 2},
29 | {"type": "parc","axe": 0,"pos": 0,"nom": "Parc Gratuit"},
30 | {"type": "propriete-junior","groupe": "Rouge","axe": 0,"pos": 1,"nom": "Astro-Hibou","colors": ["#d90e22", "#e05462"],"prix": 3},
31 | {"type": "propriete-junior","groupe": "Rouge","axe": 0,"pos": 2,"nom": "Bibou","colors": ["#d90e22", "#e05462"],"prix": 3},
32 | {"type": "chance","axe": 0,"pos": 3},
33 | {"type": "propriete-junior","groupe": "Jaune","axe": 0,"pos": 4,"nom": "Spatio-totem","colors": ["#ffef01", "#fff77e"],"prix": 3},
34 | {"type": "propriete-junior","groupe": "Jaune","axe": 0,"pos": 5,"nom": "QG","colors": ["#ffef01", "#fff77e"],"prix": 3},
35 | {"type": "prison","axe": 1,"pos": 0,"nom": "Allez en prison"},
36 | {"type": "propriete-junior","groupe": "Vert","axe": 1,"pos": 1,"nom": "Reptilo-mobile","colors": ["#08ab47", "#6aad84"],"prix": 4},
37 | {"type": "propriete-junior","groupe": "Vert","axe": 1,"pos": 2,"nom": "Gluglu","colors": ["#08ab47", "#6aad84"],"prix": 4},
38 | {"type": "chance","axe": 1,"pos": 3},
39 | {"type": "propriete-junior","groupe": "Bleu","axe": 1,"pos": 4,"nom": "Chat-bolide","colors": ["#0066bb", "#5a93c3"],"prix": 5},
40 | {"type": "propriete-junior","groupe": "Bleu","axe": 1,"pos": 5,"nom": "Yoyo","colors": ["#0066bb", "#5a93c3"],"prix": 5}
41 | ],
42 | "images":{
43 | "chance":{
44 | "src": "img/interrogation.png",
45 | "width": 50,
46 | "height": 60
47 | }
48 | },
49 | "titles":{
50 | "chance":"Chance"
51 | },
52 | "chance":{
53 | "cartes":[
54 | {"nom":"C'est votre anniversaire, chaque joueur vous donne F 1.000","type":"birthday","montant":1000},
55 | {"nom":"Faites des réparations dans toutes vos maisons : F 11.500 par hôtel et F 4.500 par maison","type":"repair","hotel":11500,"maison":4500},
56 | {"nom":"Payer pour frais de scolarité F 15.000","type":"taxe","montant":15000},
57 | {"nom":"Amende pour excès de vitesse : F 1.500","type":"taxe","montant":1500},
58 | {"nom":"Vous avez gagné le prix de mots croisés. Recevez F 10.000","type":"prime","montant":10000},
59 | {"nom":"La banque vous verse un dividende de F 5.000","type":"prime","montant":5000},
60 | {"nom":"Votre immeuble et votre prêt rapportent. Vous devez toucher F 15.000","type":"prime","montant":15000},
61 | {"nom":"Amende pour ivresse : F 2.000","type":"taxe","montant":2000},
62 | {"nom":"Rendez vous à l'avenue Henri-Martin. Si vous passez par la case Départ, recevez F 20.000","type":"goto","axe":0,"pos":4},
63 | {"nom":"Rendez vous à la Rue de la Paix","type":"goto","axe":1,"pos":9},
64 | {"nom":"Reculez de 3 cases","type":"move","nb":-3},
65 | {"nom":"Avancez jusqu'à la case Départ","type":"goto","axe":2,"pos":0},
66 | {"nom":"Rendez vous à la gare de Lyon. Si vous passez par la case Départ, recevez F 20.000","type":"goto","axe":3,"pos":5},
67 | {"nom":"Avancez au Boulevard de la Villette. Si vous passez par la case Départ, recevez F 20.000","type":"goto","axe":3,"pos":1},
68 | {"nom":"Allez en prison. Rendez vous directement à la prison. Ne franchissez par la case Départ. Ne touchez pas F 20.000","type":"goto","axe":1,"pos":0,"direct":true}
69 | ]
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/run_monopoly.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/binary"
5 | "encoding/hex"
6 | "encoding/json"
7 | "errors"
8 | "fmt"
9 | "github.com/jotitan/monopoly/dice"
10 | "io/ioutil"
11 | "log"
12 | "math/rand"
13 | "net/http"
14 | "os"
15 | "path/filepath"
16 | "strconv"
17 | "strings"
18 | "time"
19 | )
20 |
21 | var resources string
22 |
23 | func main() {
24 | if len(os.Args) != 2 {
25 | log.Fatal("Impossible to start server, need 1 argument : resources folder", os.Args)
26 | return
27 | }
28 | resources = os.Args[1]
29 | runServer()
30 | }
31 |
32 | func runServer() {
33 | server := http.NewServeMux()
34 | server.HandleFunc("/dices", runDice)
35 | server.HandleFunc("/createGame", createGame)
36 | server.HandleFunc("/game/exist", isGameExists)
37 | server.HandleFunc("/event", manageEvent)
38 | server.HandleFunc("/connect", connectGame)
39 | server.HandleFunc("/", root)
40 |
41 | log.Println("Run on 8100")
42 | http.ListenAndServe(":8100", server)
43 | }
44 |
45 | // Launch some dices and return results
46 | func runDice(w http.ResponseWriter, r *http.Request) {
47 | w.Header().Set("Access-Control-Allow-Origin", "*")
48 | w.Header().Set("Content-type", "application/json")
49 |
50 | if nb, err := strconv.Atoi(r.FormValue("nb")); err == nil {
51 | data, _ := json.Marshal(dice.Throw(int(nb)))
52 | w.Write(data)
53 | }
54 | }
55 |
56 | // Serve files
57 | func root(response http.ResponseWriter, request *http.Request) {
58 | if strings.HasSuffix(request.RequestURI[1:], ".js") {
59 | response.Header().Set("Content-Type", "text/javascript")
60 | }
61 | http.ServeFile(response, request, filepath.Join(resources, request.RequestURI[1:]))
62 | }
63 |
64 | func generateGameId() string {
65 | d := []byte(time.Now().Format("20060102150405"))
66 | r := rand.Uint64()
67 | data := make([]byte, 8)
68 | binary.LittleEndian.PutUint64(data, r)
69 | d = append(d, data...)
70 | return hex.EncodeToString(data)
71 | }
72 |
73 | func isGameExists(w http.ResponseWriter, r *http.Request) {
74 | gameID := r.FormValue("id")
75 | _, exist := games[gameID]
76 | w.Write([]byte(fmt.Sprintf("%t", exist)))
77 | }
78 |
79 | func createGame(w http.ResponseWriter, r *http.Request) {
80 | gameID := generateGameId()
81 | games[gameID] = &Game{ID: gameID, Players: make(map[string]chan []byte), counterID: 0}
82 | w.Header().Set("Content-type", "application/json")
83 | w.Write([]byte(fmt.Sprintf("{\"game\":\"%s\"}", gameID)))
84 | }
85 |
86 | // Receive an event from a player, send to others, avoid launcher
87 | // Two parameters is needed : game id and player sender id
88 | func manageEvent(w http.ResponseWriter, r *http.Request) {
89 | w.Header().Set("Access-Control-Allow-Origin", "*")
90 | if data, err := ioutil.ReadAll(r.Body); err == nil {
91 | gameID := r.FormValue("game")
92 | playerID := r.FormValue("playerID")
93 | // Dispatch event to all others gamer
94 | if err := dispatchEventToPlayers(gameID, playerID, data); err != nil {
95 | http.Error(w, err.Error(), 404)
96 | } else {
97 | w.Write([]byte("{}"))
98 | }
99 | } else {
100 | http.Error(w, "Unknown event", 404)
101 | }
102 | }
103 |
104 | func dispatchEventToPlayers(gameID, playerID string, event []byte) error {
105 | if game, exist := games[gameID]; exist {
106 | for player, chanel := range game.Players {
107 | if !strings.EqualFold(playerID, player) {
108 | chanel <- event
109 | }
110 | }
111 | return nil
112 | }
113 | return errors.New("Game with id " + gameID + " not exist")
114 | }
115 |
116 | func connectGame(w http.ResponseWriter, r *http.Request) {
117 | w.Header().Set("Content-Type", "text/event-stream")
118 | w.Header().Set("Cache-Control", "no-cache")
119 | w.Header().Set("Connection", "keep-alive")
120 | w.Header().Set("Access-Control-Allow-Origin", "*")
121 |
122 | gameID := r.FormValue("game")
123 | uniquePlayer := r.FormValue("player")
124 | if game, exist := games[gameID]; exist {
125 | game.counterID++
126 | playerID := fmt.Sprintf("player_%d", game.counterID)
127 | game.Players[playerID] = make(chan []byte, 10)
128 | if strings.EqualFold("", uniquePlayer) {
129 | writeEvent(w, "welcome", []byte("{\"message\":\"Start game\",\"playerID\":\""+playerID+"\"}"))
130 | } else {
131 | writeEvent(w, "welcome", []byte(fmt.Sprintf("{\"message\":\"Rejoin\",\"playerID\":\"%s\",\"uniquePlayerID\":\"%s\"}", playerID, uniquePlayer)))
132 | }
133 | go func() {
134 | <-r.Context().Done()
135 | close(game.Players[playerID])
136 | game.Players[playerID] = nil
137 | delete(game.Players, playerID)
138 | // If all players are disconnected, remove game
139 | if len(game.Players) == 0 {
140 | log.Println("Remove game", gameID)
141 | delete(games, gameID)
142 | } else {
143 | // notify players for disconnect
144 | dispatchEventToPlayers(gameID, "", []byte("{\"kind\":\"exit\",\"player\":\""+playerID+"\"}"))
145 | }
146 | }()
147 | for {
148 | if event, more := <-game.Players[playerID]; more {
149 | writeEvent(w, "event", event)
150 | } else {
151 | // Close the chanel
152 | log.Println("Close the chanel")
153 | break
154 | }
155 | }
156 | } else {
157 | http.Error(w, "No game exist", 404)
158 | }
159 | }
160 |
161 | func writeEvent(w http.ResponseWriter, eventName string, event []byte) {
162 | log.Println(fmt.Sprintf("Send %s : %s", eventName, string(event)))
163 | w.Write([]byte(fmt.Sprintf("event: %s\n", eventName)))
164 | w.Write([]byte("data: " + string(event) + "\n\n"))
165 | w.(http.Flusher).Flush()
166 |
167 | }
168 |
169 | var games = make(map[string]*Game, 0)
170 | var counterGame = 0
171 |
172 | // Game represent a monopoly game
173 | type Game struct {
174 | ID string
175 | // For each player, a chanel to send information. Key a map is player id
176 | Players map[string]chan []byte
177 | counterID int
178 | }
179 |
--------------------------------------------------------------------------------
/css/qunit-1.12.0.css:
--------------------------------------------------------------------------------
1 | /**
2 | * QUnit v1.12.0 - A JavaScript Unit Testing Framework
3 | *
4 | * http://qunitjs.com
5 | *
6 | * Copyright 2012 jQuery Foundation and other contributors
7 | * Released under the MIT license.
8 | * http://jquery.org/license
9 | */
10 |
11 | /** Font Family and Sizes */
12 |
13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
15 | }
16 |
17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
18 | #qunit-tests { font-size: smaller; }
19 |
20 |
21 | /** Resets */
22 |
23 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
24 | margin: 0;
25 | padding: 0;
26 | }
27 |
28 |
29 | /** Header */
30 |
31 | #qunit-header {
32 | padding: 0.5em 0 0.5em 1em;
33 |
34 | color: #8699a4;
35 | background-color: #0d3349;
36 |
37 | font-size: 1.5em;
38 | line-height: 1em;
39 | font-weight: normal;
40 |
41 | border-radius: 5px 5px 0 0;
42 | -moz-border-radius: 5px 5px 0 0;
43 | -webkit-border-top-right-radius: 5px;
44 | -webkit-border-top-left-radius: 5px;
45 | }
46 |
47 | #qunit-header a {
48 | text-decoration: none;
49 | color: #c2ccd1;
50 | }
51 |
52 | #qunit-header a:hover,
53 | #qunit-header a:focus {
54 | color: #fff;
55 | }
56 |
57 | #qunit-testrunner-toolbar label {
58 | display: inline-block;
59 | padding: 0 .5em 0 .1em;
60 | }
61 |
62 | #qunit-banner {
63 | height: 5px;
64 | }
65 |
66 | #qunit-testrunner-toolbar {
67 | padding: 0.5em 0 0.5em 2em;
68 | color: #5E740B;
69 | background-color: #eee;
70 | overflow: hidden;
71 | }
72 |
73 | #qunit-userAgent {
74 | padding: 0.5em 0 0.5em 2.5em;
75 | background-color: #2b81af;
76 | color: #fff;
77 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
78 | }
79 |
80 | #qunit-modulefilter-container {
81 | float: right;
82 | }
83 |
84 | /** Tests: Pass/Fail */
85 |
86 | #qunit-tests {
87 | list-style-position: inside;
88 | }
89 |
90 | #qunit-tests li {
91 | padding: 0.4em 0.5em 0.4em 2.5em;
92 | border-bottom: 1px solid #fff;
93 | list-style-position: inside;
94 | }
95 |
96 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
97 | display: none;
98 | }
99 |
100 | #qunit-tests li strong {
101 | cursor: pointer;
102 | }
103 |
104 | #qunit-tests li a {
105 | padding: 0.5em;
106 | color: #c2ccd1;
107 | text-decoration: none;
108 | }
109 | #qunit-tests li a:hover,
110 | #qunit-tests li a:focus {
111 | color: #000;
112 | }
113 |
114 | #qunit-tests li .runtime {
115 | float: right;
116 | font-size: smaller;
117 | }
118 |
119 | .qunit-assert-list {
120 | margin-top: 0.5em;
121 | padding: 0.5em;
122 |
123 | background-color: #fff;
124 |
125 | border-radius: 5px;
126 | -moz-border-radius: 5px;
127 | -webkit-border-radius: 5px;
128 | }
129 |
130 | .qunit-collapsed {
131 | display: none;
132 | }
133 |
134 | #qunit-tests table {
135 | border-collapse: collapse;
136 | margin-top: .2em;
137 | }
138 |
139 | #qunit-tests th {
140 | text-align: right;
141 | vertical-align: top;
142 | padding: 0 .5em 0 0;
143 | }
144 |
145 | #qunit-tests td {
146 | vertical-align: top;
147 | }
148 |
149 | #qunit-tests pre {
150 | margin: 0;
151 | white-space: pre-wrap;
152 | word-wrap: break-word;
153 | }
154 |
155 | #qunit-tests del {
156 | background-color: #e0f2be;
157 | color: #374e0c;
158 | text-decoration: none;
159 | }
160 |
161 | #qunit-tests ins {
162 | background-color: #ffcaca;
163 | color: #500;
164 | text-decoration: none;
165 | }
166 |
167 | /*** Test Counts */
168 |
169 | #qunit-tests b.counts { color: black; }
170 | #qunit-tests b.passed { color: #5E740B; }
171 | #qunit-tests b.failed { color: #710909; }
172 |
173 | #qunit-tests li li {
174 | padding: 5px;
175 | background-color: #fff;
176 | border-bottom: none;
177 | list-style-position: inside;
178 | }
179 |
180 | /*** Passing Styles */
181 |
182 | #qunit-tests li li.pass {
183 | color: #3c510c;
184 | background-color: #fff;
185 | border-left: 10px solid #C6E746;
186 | }
187 |
188 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
189 | #qunit-tests .pass .test-name { color: #366097; }
190 |
191 | #qunit-tests .pass .test-actual,
192 | #qunit-tests .pass .test-expected { color: #999999; }
193 |
194 | #qunit-banner.qunit-pass { background-color: #C6E746; }
195 |
196 | /*** Failing Styles */
197 |
198 | #qunit-tests li li.fail {
199 | color: #710909;
200 | background-color: #fff;
201 | border-left: 10px solid #EE5757;
202 | white-space: pre;
203 | }
204 |
205 | #qunit-tests > li:last-child {
206 | border-radius: 0 0 5px 5px;
207 | -moz-border-radius: 0 0 5px 5px;
208 | -webkit-border-bottom-right-radius: 5px;
209 | -webkit-border-bottom-left-radius: 5px;
210 | }
211 |
212 | #qunit-tests .fail { color: #000000; background-color: #EE5757; }
213 | #qunit-tests .fail .test-name,
214 | #qunit-tests .fail .module-name { color: #000000; }
215 |
216 | #qunit-tests .fail .test-actual { color: #EE5757; }
217 | #qunit-tests .fail .test-expected { color: green; }
218 |
219 | #qunit-banner.qunit-fail { background-color: #EE5757; }
220 |
221 |
222 | /** Result */
223 |
224 | #qunit-testresult {
225 | padding: 0.5em 0.5em 0.5em 2.5em;
226 |
227 | color: #2b81af;
228 | background-color: #D2E0E6;
229 |
230 | border-bottom: 1px solid white;
231 | }
232 | #qunit-testresult .module-name {
233 | font-weight: bold;
234 | }
235 |
236 | /** Fixture */
237 |
238 | #qunit-fixture {
239 | position: absolute;
240 | top: -10000px;
241 | left: -10000px;
242 | width: 1000px;
243 | height: 1000px;
244 | }
245 |
--------------------------------------------------------------------------------
/poc/slider.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Bonjour
12 |
13 |
14 |
15 |
16 |
17 |
169 |
170 |
171 |
--------------------------------------------------------------------------------
/poc/bingo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Tirage
12 | Boule
13 | Tri
14 | Table
15 |
16 |
17 |
21 |
22 |
23 |
24 |
83 |
84 |
216 |
217 |
218 |
219 |
--------------------------------------------------------------------------------
/js/core/gestion_constructions.js:
--------------------------------------------------------------------------------
1 | /* Gere les reserves de constructions (maison / hotel) */
2 | let GestionConstructions = {
3 | nbInitHouse: 32,
4 | nbInitHotel: 12,
5 | nbSellHouse: 0,
6 | nbSellHotel: 0,
7 | reset: function () {
8 | this.nbInitHouse = 32;
9 | this.nbInitHotel = 12;
10 | this.nbSellHouse = 0;
11 | this.nbSellHotel = 0;
12 | },
13 | isFreeHouse: function () {
14 | return this.nbInitHouse > this.nbSellHouse;
15 | },
16 | isFreeHotel: function (nbActualHouse) {
17 | if (nbActualHouse == null) {
18 | return this.nbInitHotel > this.nbSellHotel;
19 | }
20 | const needHouse = 4 - nbActualHouse;
21 | return this.nbInitHotel > this.nbSellHotel & this.nbInitHouse >= this.nbSellHouse + needHouse;
22 | },
23 | getRestHouse: function () {
24 | return this.nbInitHouse - this.nbSellHouse;
25 | },
26 | getRestHotel: function () {
27 | return this.nbInitHotel - this.nbSellHotel;
28 | },
29 | sellHotel:function(){
30 | this.nbSellHotel--;
31 | },
32 | sellHouse:function(){
33 | this.nbSellHouse--;
34 | },
35 | buyHouse: function () {
36 | if (!this.isFreeHouse()) {
37 | throw "Impossible d'acheter une maison."
38 | }
39 | this.nbSellHouse++;
40 | },
41 | buyHouses: function (nb) {
42 | if (nb > this.getRestHouse()) {
43 | throw "Impossible d'acheter les maisons."
44 | }
45 | this.nbSellHouse += nb;
46 | },
47 | /* Achete / vend le nombre d'hotels indiques (verification simple sur le nombre d'hotel) */
48 | buyHotels: function (nb) {
49 | if (nb > this.getRestHotel()) {
50 | throw "Impossible d'acheter les hotels."
51 | }
52 | this.nbSellHotel += nb;
53 | },
54 | buyHotel: function () {
55 | if (!this.isFreeHouse()) {
56 | throw "Impossible d'acheter un hotel."
57 | }
58 | this.nbSellHotel++;
59 | // On libere les maisons liees (4)
60 | this.nbSellHouse -= 4;
61 | },
62 | /* Calcule les restes finaux en maison / hotel. Renvoie egalement le delta */
63 | // On calcule en premier les ventes de maisons
64 | // On calcule les achats d'hotels (qui necessitent des maisons puis des hotels)
65 | // Pour finir, on calcule l'achat de maison
66 | // Chaque projet contient la couleur du groupe, le from (nb, type) et le to (nb, type)
67 | simulateBuy: function (projects) {
68 | // Projects est un tableau de from:{type,nb},to:{type,nb}
69 | let simulation = {
70 | achat: {
71 | maison: 0,
72 | hotel: 0
73 | },
74 | reste: {
75 | maison: this.nbInitHouse - this.nbSellHouse,
76 | hotel: this.nbInitHotel - this.nbSellHotel
77 | }
78 | };
79 | let actions = {
80 | venteMaison: function (p, simulation) {
81 | if (p.from.type === "maison" && p.to.type === "maison" && p.from.nb > p.to.nb) {
82 | changeMaison(p,simulation);
83 | }
84 | },
85 | achatHotel: function (p, simulation) {
86 | // Pour valider un hotel, il faut que les autres proprietes aient au moins 4 maisons. On les achete maintenant si on les trouve
87 | if (p.from.type === "maison" && p.to.type === "hotel") {
88 | // On achete les maisons sur le meme groupe s'il y en a
89 | for (let project in projects) {
90 | let p2 = projects[project];
91 | if (p2.color === p.color && p2.from.type === "maison" && p2.to.type === "maison" && p2.to.nb === 4) {
92 | // On les achete maintenant
93 | actions.achatMaison(p2, simulation);
94 | }
95 | }
96 | // Verifie qu'il y a assez de maison disponible
97 | const resteMaison = 4 - p.from.nb;
98 | if (resteMaison > simulation.reste.maison) {
99 | // Impossible, pas assez de maison, on renvoie un nombre de maison negatif et on sort
100 | simulation.reste.maison -= resteMaison;
101 | return;
102 | } else {
103 | // On achete un hotel
104 | simulation.reste.hotel--;
105 | simulation.achat.hotel++;
106 | simulation.reste.maison += p.from.nb;
107 | simulation.achat.maison -= p.from.nb;
108 | }
109 | p.done = true;
110 | }
111 | },
112 | venteHotel: function (p, simulation) {
113 | if (p.from.type === "hotel" && p.to.type === "maison") {
114 | // Verifie qu'il y a assez de maison disponible
115 | if (p.to.nb > simulation.reste.maison) {
116 | // Impossible, pas assez de maison, on renvoie un nombre de maison negatif et on sort
117 | simulation.reste.maison -= p.to.nb;
118 | return;
119 | } else {
120 | // On vend l'hotel et on place des maisons
121 | simulation.reste.hotel++;
122 | simulation.achat.hotel--;
123 | simulation.reste.maison -= p.to.nb;
124 | simulation.achat.maison += p.to.nb;
125 | }
126 | p.done = true;
127 | }
128 | },
129 | achatMaison: function (p, simulation) {
130 | if (p.from.type === "maison" && p.to.type === "maison" && p.from.nb < p.to.nb) {
131 | changeMaison(p,simulation);
132 | }
133 | }
134 | };
135 | let changeMaison = (p,simulation)=>{
136 | let nb = p.from.nb - p.to.nb;
137 | simulation.achat.maison -= nb;
138 | simulation.reste.maison += nb;
139 | p.done = true;
140 | }
141 | for (let a in actions) {
142 | let action = actions[a];
143 | for (let index in projects) {
144 | let p = projects[index];
145 | if (p.done == null) {
146 | try {
147 | action(p, simulation);
148 | } catch (e) {
149 | // Exception levee si le traitement doit etre interrompu
150 | console.log("exception",e);
151 | return simulation;
152 | }
153 | }
154 | }
155 | }
156 | return simulation;
157 | }
158 | }
159 |
160 | export {GestionConstructions};
--------------------------------------------------------------------------------
/test/test-maisons-js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Test Monopoly
4 |
5 |
6 |
7 |
8 |
199 |
200 |
201 | Lien jeu
202 |
203 |
204 |
205 |
206 |
--------------------------------------------------------------------------------
/js/entity/comportement.js:
--------------------------------------------------------------------------------
1 | /* Gestion du comportement*/
2 |
3 | import {GestionFiche} from "../display/case_jeu.js";
4 | import {GestionJoueur} from "../core/gestion_joueurs.js";
5 |
6 | /* @Abstract */
7 | /* Objet qui gere le comportement (rapport a l'argent). Integre la prise de risque (position du jour) */
8 | /* @risque : prise de risque entre 0 et 1 */
9 |
10 | class Comportement {
11 | constructor(risque, name, id) {
12 | this.risque = risque;
13 | this.probaDes = [0, 2.77, 5.55, 8.33, 11.1, 13.8, 16.7, 13.8, 11.1, 8.33, 5.55, 2.77];
14 | this.name = name;
15 | this.id = id;
16 | }
17 |
18 | /* Indique le risque global a depenser cette somme pour le joueur */
19 | /* Se base sur 3 informations :
20 | 1 : le montant a depenser par rapport a l'argent disponible.
21 | 2 : le risque de tomber prochainement sur un loyer eleve
22 | 3 : le cout du plus fort loyer du plateau
23 | Plus le risque est grand, plus il est important
24 | */
25 | getRisqueTotal(joueur, cout) {
26 | let risque1 = this.calculMargeMontant(joueur, cout);
27 | let risque2 = this.calculRisque(joueur, joueur.montant);
28 |
29 | return risque1 * (risque2 / 100 + 1);
30 | }
31 |
32 | /* Determine le budget max pour un indicateur de strategie donne */
33 | /* Plafonne l'enchere max (comme les propositions ?) */
34 | getMaxBudgetForStrategie (joueur, strategieValue) {
35 | // Renvoie la valeur du cout pour que getRisqueTotal = strategieValue
36 | let risque = this.calculRisque(joueur, joueur.montant);
37 | let marge = strategieValue / (risque / 100 + 1);
38 | // On ajoute un random sur 10% du prix => moins previsible
39 | let montant = this.findCoutFromFixMarge(joueur, marge);
40 | montant+=Math.round((montant*0.1)*(((Math.random()* 1000)%10 -5)/10));
41 | return Math.min(montant, joueur.montant - joueur.minimumPriceHouse());
42 | }
43 |
44 | /* Appele lorsqu'une proposition a deja ete faite et qu'elle etait insuffisante */
45 | getFactorForProposition() {
46 | return 1 + this.risque;
47 | }
48 |
49 | /* Calcul le budget depensable pour la construction de maison / hotel */
50 | /* Prendre en compte l'achat potentiel de nouveau terrain. Pour la strategie, on calcule les terrains qui interessent */
51 | /* @param forceHypotheque : si vrai, on force l'usage de l'argent dispo apres hypotheque */
52 | getBudget (joueur, forceHypotheque) {
53 | let assiette = joueur.montant; // Utilise pour calculer les risques
54 | // Si le joueur est une charogne, on utilise l'argent dispo avec les possibles hypotheques (tous les terrains sauf les groupes).
55 | // Utilise uniquement pour le calcul de risque, pas pour l'achat (pour ne pas hypothequer lors de l'achat).
56 | if (forceHypotheque === true || this.risque > 0.6) {
57 | assiette = joueur.getStats().argentDispoHypo;
58 | }
59 | // On prend le plus fort loyer du plateau
60 | let maxLoyer = this.plusFortLoyer(joueur);
61 | // On prend l'argent pondere par le risque
62 | let risque = this.calculRisque(joueur, assiette);
63 | // On pondere le loyer max par le carre du risque afin d'augmenter exponentiellement son importance
64 | return Math.round((joueur.montant - maxLoyer * (1 - this.risque * this.risque)) * (1 - risque / 100));
65 | }
66 |
67 | /* Calcul le terrain du joueur sur lesquels les adversaires peuvent tomber */
68 | /* @param seuil : seuil a partir duquel on renvoie les maisons */
69 | getNextProprietesVisitees (joueur) { //,seuil){
70 | let maisons = [];
71 | GestionJoueur.forEach(j=>{
72 | if (!j.equals(joueur)) {
73 | // On parcours toutes les statistiques et on mesure le risque de tomber sur une propriete du joueur
74 | for (let i = 1; i < 12; i++) {
75 | let fiche = GestionFiche.get(j.pion.deplaceValeursDes(i));
76 | if (fiche.isTerrain() && fiche.joueurPossede != null && fiche.joueurPossede.equals(joueur)) {
77 | //maison visitable, on ajoute la maison avec la proba
78 | if (maisons[fiche.id] != null) {
79 | maisons[fiche.id].proba += this.probaDes[i] / 100;
80 | } else {
81 | maisons[fiche.id] = ({
82 | proba: this.probaDes[i] / 100,
83 | maison: fiche
84 | });
85 | }
86 | }
87 | }
88 | }
89 | },this);
90 | return maisons;
91 | }
92 |
93 | /* Calcule la marge d'achat par rapport au montant et le pondere par rapport a la prise de risque. */
94 | /* Plus il est grand, plus c'est risque */
95 | calculMargeMontant (joueur, cout) {
96 | let marge = cout / joueur.montant; // inferieur a 1
97 | return marge / this.risque;
98 | }
99 |
100 | /* Calcul le cout pour une marge donnee */
101 | findCoutFromFixMarge (joueur, marge) {
102 | return (marge * this.risque) * joueur.montant;
103 | }
104 |
105 | /* Se base sur les prochaines cases a risque qui arrive, renvoi un pourcentage */
106 | calculRisque (joueur, argent) {
107 | // On calcul le risque de tomber sur une case cher.
108 | // On considere un risque quand on est au dessus de risque * montant d'amende)
109 | let position = joueur.pion.position;
110 | let axe = joueur.pion.axe;
111 | let stats = 0;
112 | for (let i = 1; i <= 12; i++) {
113 | let pos = GestionFiche.nextPos(axe, position);
114 | axe = pos.axe;
115 | position = pos.position;
116 | let fiche = GestionFiche.getById(axe + "-" + position);
117 | if (fiche != null && fiche.getLoyer != null && fiche.joueurPossede != null && !fiche.joueurPossede.equals(joueur) && (fiche.getLoyer() > (argent * this.risque))) {
118 | stats += this.probaDes[i - 1];
119 | }
120 | }
121 | return stats;
122 | }
123 |
124 | // calcul le loyer le plus fort du joueur (et n'appartenant pas au joueur). Permet de connaitre la treso max que le joueur peut posseder sur lui
125 | plusFortLoyer (joueur) {
126 | let max = joueur.montantDepart; // Prix de l'impot sur le revenu, comme le depart
127 | let it = GestionFiche.iteratorTerrains();
128 | while (it.hasNext()) {
129 | let f = it.next();
130 | if (f.getLoyer != null && f.joueurPossede != null && !joueur.equals(f.joueurPossede) && f.getLoyer() > max) {
131 | max = f.getLoyer();
132 | }
133 | }
134 | return max;
135 | }
136 |
137 | // calcul le loyer moyen que peut rencontrer le joueur
138 | getLoyerMoyen (joueur) {
139 | let montant = joueur.montantDepart; // Prix de l'impot sur le revenu, comme le depart
140 | let nb = 1;
141 | let it = GestionFiche.iteratorTerrains();
142 | while (it.hasNext()) {
143 | let f = it.next();
144 | if (f.getLoyer != null && f.joueurPossede != null && !joueur.equals(f.joueurPossede)) {
145 | montant += f.getLoyer();
146 | nb++;
147 | }
148 | }
149 | return {
150 | montant: montant / nb,
151 | nb: nb
152 | };
153 | }
154 | }
155 |
156 | /* Implementations */
157 |
158 | class CheapComportement extends Comportement {
159 | constructor(){
160 | super(0.25, "Prudent", 0);
161 | }
162 | }
163 |
164 | class MediumComportement extends Comportement {
165 | constructor() {
166 | super(0.5, "Normal", 1);
167 | }
168 | }
169 |
170 | class HardComportement extends Comportement {
171 | constructor() {
172 | super(0.8, "Fou", 2);
173 | }
174 | }
175 |
176 | let GestionComportement = {
177 | comportements : [CheapComportement,MediumComportement,HardComportement],
178 | create:function(id){
179 | return new this.comportements[id]();
180 | },
181 | createRandom:function(){
182 | return this.create(Math.round(Math.random() * 1000)%this.comportements.length);
183 | },
184 | getAll:function(){
185 | return this.comportements;
186 | },
187 | length:function(){
188 | return this.comportements.length;
189 | }
190 | };
191 |
192 | export {GestionComportement};
--------------------------------------------------------------------------------
/data/data-monopoly-jamesbond.json:
--------------------------------------------------------------------------------
1 | {
2 | "plateau":{
3 | "subtitle":"James Bond Edition",
4 | "textColor":"white",
5 | "backgroundColor": "#999999",
6 | "background-image":"",
7 | "nomsJoueurs":["Daniel Craig","Roger Moore","Sean Connery","Timothy Dalton","Pierce Brosnan","Georges Lazenby"],
8 | "rollColor":"#444444",
9 | "depart":20000
10 | },
11 | "currency": "£",
12 | "fiches": [
13 | {"type": "depart","axe": 2,"pos": 0,"nom": "Départ"},
14 | {"type": "propriete","groupe": "Violet","axe": 2,"pos": 1,"nom": "Docteur No","colors": ["#812B5C", "#DFA4C6"],"prix": 4000,"loyers": [200,1000,3000,9000,16000,25000],"prixMaison": 5000},
15 | {"type": "communaute","axe": 2,"pos": 2},
16 | {"type": "propriete","groupe": "Violet","axe": 2,"pos": 3,"nom": "Bons baisers de Russie","colors": ["#812B5C", "#DFA4C6"],"prix": 8000,"loyers": [400, 2000, 6000, 18000, 32000, 45000],"prixMaison": 5000},
17 | {"type": "taxe","axe": 2,"pos": 4,"nom": "Impots sur le revenu","prix": 20000},
18 | {"type": "gare","axe": 2,"pos": 5,"nom": "Aston Martin DB5","colors": ["#000000", "#ABABAB"],"prix": 20000,"loyers": [2500, 5000, 10000, 20000]},
19 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 6,"nom": "Goldfinger","colors": ["#119AEB", "#98CEEE"],"prix": 10000,"loyers": [600, 3000, 9000, 27000, 40000, 55000],"prixMaison": 5000},
20 | {"type": "chance","axe": 2,"pos": 7},
21 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 8,"nom": "Opération Tonnerre","colors": ["#119AEB", "#98CEEE"],"prix": 10000,"loyers": [600, 3000, 9000, 27000, 40000, 55000],"prixMaison": 5000},
22 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 9,"nom": "On ne vit que deux fois","colors": ["#119AEB", "#98CEEE"],"prix": 12000,"loyers": [800, 4000, 10000, 30000, 45000, 60000],"prixMaison": 5000},
23 | {"type": "special","axe": 3,"pos": 0,"nom": "Simple visite"},
24 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 1,"nom": "Au Service secret de sa Majesté","colors": ["#73316F", "#DFB4DC"],"prix": 14000,"loyers": [1000, 5000, 15000, 45000, 62500, 75000],"prixMaison": 10000},
25 | {"type": "compagnie","axe": 3,"pos": 2,"nom": "Universal Export","img":"compagnie-universal","colors": ["lightgreen"],"prix": 15000,"loyers": [400,1000]},
26 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 3,"nom": "Les Diamants sont Eternels","colors": ["#73316F", "#DFB4DC"],"prix": 14000,"loyers": [1000, 5000, 15000, 45000, 62500, 75000],"prixMaison": 10000},
27 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 4,"nom": "Vivre et Laisser Mourir","colors": ["#73316F", "#DFB4DC"],"prix": 16000,"loyers": [1200, 6000, 18000, 50000, 70000, 90000],"prixMaison": 10000},
28 | {"type": "gare","axe": 3,"pos": 5,"nom": "Aston Martin AMV8","colors": ["#000000", "#ABABAB"],"prix": 20000,"loyers": [2500, 5000, 10000, 20000]},
29 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 6,"nom": "L'homme au Pistolet d'Or","colors": ["#D16E2D", "#FECC84"],"prix": 18000,"loyers": [1400, 7000, 20000, 55000, 75000, 95000],"prixMaison": 10000},
30 | {"type": "communaute","axe": 3,"pos": 7},
31 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 8,"nom": "L'espion qui m'aimait","colors": ["#D16E2D", "#FECC84"],"prix": 18000,"loyers": [1400, 7000, 20000, 55000, 75000, 95000],"prixMaison": 10000},
32 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 9,"nom": "Moonraker","colors": ["#D16E2D", "#FECC84"],"prix": 20000,"loyers": [1600, 8000, 22000, 60000, 80000, 100000],"prixMaison": 10000},
33 | {"type": "parc","axe": 0,"pos": 0,"nom": "Parc Gratuit"},
34 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 1,"nom": "Rien que pour vos yeux","colors": ["#D32C19", "#F9AEA6"],"prix": 22000,"loyers": [1800, 9000, 25000, 70000, 87500, 105000],"prixMaison": 15000},
35 | {"type": "chance","axe": 0,"pos": 2},
36 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 3,"nom": "Octopussy","colors": ["#D32C19", "#F9AEA6"],"prix": 22000,"loyers": [1800, 9000, 25000, 70000, 87500, 105000],"prixMaison": 15000},
37 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 4,"nom": "Dangereu- sement vôtre","colors": ["#D32C19", "#F9AEA6"],"prix": 24000,"loyers": [2000, 10000, 30000, 75000, 92500, 110000],"prixMaison": 15000},
38 | {"type": "gare","axe": 0,"pos": 5,"nom": "Aston Martin Vanquish","colors": ["#000000", "#ABABAB"],"prix": 20000,"loyers": [2500, 5000, 10000, 20000]},
39 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 6,"nom": "Tuer n'est pas jouer","colors": ["#E6E018", "#F8F587"],"prix": 26000,"loyers": [2200, 11000, 33000, 80000, 97500, 115000],"prixMaison": 15000},
40 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 7,"nom": "Permis de Tuer","colors": ["#E6E018", "#F8F587"],"prix": 26000,"loyers": [2200, 11000, 33000, 80000, 97500, 115000],"prixMaison": 15000},
41 | {"type": "compagnie","axe": 0,"pos": 8,"nom": "S.P.E.C.T.R.E.","img":"compagnie-spectre","colors": ["lightgreen"],"prix": 15000,"loyers": [400,1000]},
42 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 9,"nom": "Goldeneye","colors": ["#E6E018", "#F8F587"],"prix": 28000,"loyers": [2400, 12000, 36000, 85000, 102500, 120000],"prixMaison": 15000},
43 | {"type": "prison","axe": 1,"pos": 0,"nom": "Allez en prison"},
44 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 1,"nom": "Demain ne Meurt Jamais","colors": ["#11862E", "#93D1A2"],"prix": 30000,"loyers": [2600, 13000, 39000, 90000, 110000, 127500],"prixMaison": 20000},
45 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 2,"nom": "Le Monde ne Suffit pas","colors": ["#11862E", "#93D1A2"],"prix": 30000,"loyers": [2600, 13000, 39000, 90000, 110000, 127500],"prixMaison": 20000},
46 | {"type": "communaute","axe": 1,"pos": 3},
47 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 4,"nom": "Meurs un Autre Jour","colors": ["#11862E", "#93D1A2"],"prix": 32000,"loyers": [2800, 15000, 45000, 100000, 120000, 140000],"prixMaison": 20000},
48 | {"type": "gare","axe": 1,"pos": 5,"nom": "Aston Martin DBS","colors": ["#000000", "#ABABAB"],"prix": 20000,"loyers": [2500, 5000, 10000, 20000]},
49 | {"type": "chance","axe": 1,"pos": 6},
50 | {"type": "propriete","groupe": "Bleu","axe": 1,"pos": 7,"nom": "Casino Royale","colors": ["#132450", "#808EB0"],"prix": 35000,"loyers": [3500, 17500, 50000, 110000, 130000, 150000],"prixMaison": 20000},
51 | {"type": "taxe","axe": 1,"pos": 8,"nom": "Champagne","prix": 10000},
52 | {"type": "propriete","groupe": "Bleu","axe": 1,"pos": 9,"nom": "Quantum of Solace","colors": ["#132450", "#808EB0"],"prix": 40000,"loyers": [5000, 20000, 60000, 140000, 170000, 200000],"prixMaison": 20000}
53 | ],
54 | "images":{
55 | "gare":{
56 | "src" : "img/jamesbond/aston_martin.png",
57 | "width":60,
58 | "height":20,
59 | "margin":15
60 | },
61 | "chance":{
62 | "src" : "img/jamesbond/gadget.png",
63 | "width":32,
64 | "height":70,
65 | "margin":-5
66 | },
67 | "caisseDeCommunaute":{
68 | "src" : "img/jamesbond/woman.png",
69 | "width":50,
70 | "height":60,
71 | "margin":0
72 | },
73 | "compagnie-spectre":{
74 | "src" : "img/jamesbond/spectre.png",
75 | "width":45,
76 | "height":45,
77 | "margin":0
78 | },
79 | "compagnie-universal":{
80 | "src" : "img/jamesbond/universal_export.png",
81 | "width":40,
82 | "height":40,
83 | "margin":5
84 | }
85 | },
86 | "titles":{
87 | "chance":"Gadgets",
88 | "communaute":"James Bond Girl",
89 | "taxe":"Champagne"
90 | },
91 | "chance":{
92 | "cartes":[
93 | {"nom":"L'aile avant de la DBS a été abimée, montant des réparations, £ 15.000","type":"taxe","montant":15000},
94 | {"nom":"Amende pour excès de vitesse : £ 1.500","type":"taxe","montant":1500},
95 | {"nom":"Casino de Monte Carlo, vous gagner 10000£ au bacarra","type":"prime","montant":10000},
96 | {"nom":"Une partie de poker improvisée sur un yacht vous rapporte £ 5.000","type":"prime","montant":5000},
97 | {"nom":"Vous récupérez la malette d'un terroriste, elle contient £ 15.000","type":"prime","montant":15000},
98 | {"nom":"Amende pour ivresse : £ 2.000","type":"taxe","montant":2000},
99 | {"nom":"Rendez vous à la Perlas de las Dunas","type":"goto","axe":1,"pos":9},
100 | {"nom":"Q a un cadeau pour vous, avancez jusqu'à la case Départ","type":"goto","axe":2,"pos":0},
101 | {"nom":"Regardez OHMSS. Si vous passez par la case Départ, recevez £ 20.000","type":"goto","axe":3,"pos":1},
102 | {"nom":"Allez en prison. Rendez vous directement à la prison. Ne franchissez par la case Départ. Ne touchez pas £ 20.000","type":"goto","axe":1,"pos":0,"direct":true}
103 | ]
104 | },
105 | "communaute":{
106 | "cartes":[
107 | {"nom":"Une partie de golf vous rapporte £ 5.000","type":"prime","montant":5000},
108 | {"nom":"Excès de vitesse, payer £ 5.000","type":"taxe","montant":5000},
109 | {"nom":"Prime du MI6 pour service rendu £ 10.000","type":"prime","montant":10000},
110 | {"nom":"Toujours un double six au backgammon. Recevez £ 1.000","type":"prime","montant":1000},
111 | {"nom":"Les faux papiers coutent chers £ 5.000","type":"taxe","montant":5000},
112 | {"nom":"Sanchez vous remercie pour vos informations, il vous donne £ 2.000","type":"prime","montant":2000},
113 | {"nom":"Erreur de la Banque en votre faveur Recevez £ 20.000","type":"prime","montant":2000},
114 | {"nom":"Recevez votre intérêt sur l'emprunt à 7 % £ 2.500","type":"prime","montant":2500},
115 | {"nom":"Vous détruisez vos gadgets, Q demande £ 10.000","type":"taxe","montant":10000},
116 | {"nom":"Vous héritez de £ 10.000","type":"prime","montant":10000}
117 | ]
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/data/data-monopoly-starwars.json:
--------------------------------------------------------------------------------
1 | {
2 | "plateau":{
3 | "subtitle":"Star Wars",
4 | "backgroundColor": "#888888",
5 | "textColor": "#FFFFFF",
6 | "nomsJoueurs":["R2 D2","Dark Vador","Jango Feet","StormTrooper","Boba Feet","Clone","Chewbacca"],
7 | "colors":["#2551A9","#000000","#800000","#FFFFFF","#7A9B94","#FFD300","#7A9B94","#9EA3A5"],
8 | "imgJoueurs":["img/starwars/r2_d2.png","img/starwars/darth_vader.png","img/starwars/jango_fett.png","img/starwars/stormtrooper.png","img/starwars/boba_fett.png","img/starwars/clone_2.png"],
9 | "rollColor":"#999999",
10 | "depart":200,
11 | "argent":1500,
12 | "prison":50
13 | },
14 | "currency":"7",
15 | "background-image":"",
16 | "fiches": [
17 | {"type": "depart","axe": 2,"pos": 0,"nom": "Départ"},
18 | {"type": "propriete","groupe": "Violet","axe": 2,"pos": 1,"nom": "Tatooine Mos Eisley","colors": ["#812B5C", "#DFA4C6"],"prix": 40,"loyers": [2,10,30,90,160,250],"prixMaison": 50},
19 | {"type": "communaute","axe": 2,"pos": 2},
20 | {"type": "propriete","groupe": "Violet","axe": 2,"pos": 3,"nom": "tatooine Mos Espa","colors": ["#812B5C", "#DFA4C6"],"prix": 80,"loyers": [4, 20, 60, 180, 320, 450],"prixMaison": 50},
21 | {"type": "taxe","axe": 2,"pos": 4,"nom": "Impots sur le revenu","prix": 200},
22 | {"type": "gare","axe": 2,"pos": 5,"nom": "Corellian Corvette","colors": ["#000000", "#ABABAB"],"prix": 200,"loyers": [25, 50, 100, 200]},
23 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 6,"nom": "Dagobah","colors": ["#119AEB", "#98CEEE"],"prix": 100,"loyers": [6, 30, 90, 270, 400, 550],"prixMaison": 50},
24 | {"type": "chance","axe": 2,"pos": 7},
25 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 8,"nom": "Endor","colors": ["#119AEB", "#98CEEE"],"prix": 100,"loyers": [6, 30, 90, 270, 400, 550],"prixMaison": 50},
26 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 9,"nom": "Bespin","colors": ["#119AEB", "#98CEEE"],"prix": 120,"loyers": [8, 40, 100, 300, 450, 600],"prixMaison": 50},
27 | {"type": "special","axe": 3,"pos": 0,"nom": "Simple visite"},
28 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 1,"nom": "Hoth","colors": ["#73316F", "#DFB4DC"],"prix": 140,"loyers": [10, 50, 150, 450, 625, 750],"prixMaison": 100},
29 | {"type": "compagnie","axe": 3,"pos": 2,"nom": " Death Star","colors": ["lightgreen"],"prix": 150,"loyers": [4,10]},
30 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 3,"nom": "Dantooine","colors": ["#73316F", "#DFB4DC"],"prix": 140,"loyers": [10, 50, 150, 450, 625, 750],"prixMaison": 100},
31 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 4,"nom": "Yavin 4","colors": ["#73316F", "#DFB4DC"],"prix": 160,"loyers": [12, 60, 180, 500, 700, 900],"prixMaison": 100},
32 | {"type": "gare","axe": 3,"pos": 5,"nom": "Imperial Star Destroyer","colors": ["#000000", "#ABABAB"],"prix": 200,"loyers": [25, 50, 100, 200]},
33 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 6,"nom": "Kashyyyk","colors": ["#D16E2D", "#FECC84"],"prix": 180,"loyers": [14, 70, 200, 550, 750, 950],"prixMaison": 100},
34 | {"type": "communaute","axe": 3,"pos": 7},
35 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 8,"nom": "Mustafar","colors": ["#D16E2D", "#FECC84"],"prix": 180,"loyers": [14, 70, 200, 550, 750, 950],"prixMaison": 100},
36 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 9,"nom": "Utapau","colors": ["#D16E2D", "#FECC84"],"prix": 200,"loyers": [16, 80, 220, 600, 800, 1000],"prixMaison": 100},
37 | {"type": "parc","axe": 0,"pos": 0,"nom": "Parc Gratuit"},
38 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 1,"nom": "Kessel","colors": ["#D32C19", "#F9AEA6"],"prix": 220,"loyers": [18, 90, 250, 700, 875, 1050],"prixMaison": 100},
39 | {"type": "chance","axe": 0,"pos": 2},
40 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 3,"nom": "Sullust","colors": ["#D32C19", "#F9AEA6"],"prix": 220,"loyers": [18, 9000, 250, 700, 875, 1000],"prixMaison": 150},
41 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 4,"nom": "Ord Mantell","colors": ["#D32C19", "#F9AEA6"],"prix": 240,"loyers": [20, 100, 300, 750, 925, 1100],"prixMaison": 150},
42 | {"type": "gare","axe": 0,"pos": 5,"nom": "Republic Assault Ship","colors": ["#000000", "#ABABAB"],"prix": 200,"loyers": [25, 50, 100, 200]},
43 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 6,"nom": "Geonosis","colors": ["#E6E018", "#F8F587"],"prix": 260,"loyers": [20, 110, 330, 800, 975, 1150],"prixMaison": 150},
44 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 7,"nom": "Kamino","colors": ["#E6E018", "#F8F587"],"prix": 260,"loyers": [22, 110, 300, 800, 975, 1150],"prixMaison": 150},
45 | {"type": "compagnie","axe": 0,"pos": 8,"nom": " Death Star II","colors": ["lightgreen"],"prix": 150,"loyers": [4,10]},
46 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 9,"nom": "Naboo","colors": ["#E6E018", "#F8F587"],"prix": 280,"loyers": [24, 120, 360, 850, 1025, 1200],"prixMaison": 150},
47 | {"type": "prison","axe": 1,"pos": 0,"nom": "Allez en prison"},
48 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 1,"nom": "Mon Calamari","colors": ["#11862E", "#93D1A2"],"prix": 300,"loyers": [26, 130, 390, 900, 1100, 1275],"prixMaison": 200},
49 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 2,"nom": "Corellia","colors": ["#11862E", "#93D1A2"],"prix": 300,"loyers": [26, 130, 300, 900, 1100, 1275],"prixMaison": 200},
50 | {"type": "communaute","axe": 1,"pos": 3},
51 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 4,"nom": "Alderaan","colors": ["#11862E", "#93D1A2"],"prix": 320,"loyers": [28, 150, 450, 1000, 1200, 1400],"prixMaison": 200},
52 | {"type": "gare","axe": 1,"pos": 5,"nom": "Super Star destroyer","colors": ["#000000", "#ABABAB"],"prix": 200,"loyers": [25, 50, 100, 200]},
53 | {"type": "chance","axe": 1,"pos": 6},
54 | {"type": "propriete","groupe": "Bleu","axe": 1,"pos": 7,"nom": "Coruscant Jedi Temple","colors": ["#132450", "#808EB0"],"prix": 350,"loyers": [35, 175, 500, 1100, 1300, 1500],"prixMaison": 200},
55 | {"type": "taxe","axe": 1,"pos": 8,"nom": "Taxe de luxe","prix": 100},
56 | {"type": "propriete","groupe": "Bleu","axe": 1,"pos": 9,"nom": "Coruscant Senate","colors": ["#132450", "#808EB0"],"prix": 400,"loyers": [50, 200, 600, 1400, 1700, 2000],"prixMaison": 200}
57 | ],
58 | "images":{
59 | "taxe": {
60 | "src": "img/bijou.png",
61 | "width": 40,
62 | "height": 50
63 | },
64 | "chance":{
65 | "src": "img/starwars/sith_light_saber.png",
66 | "width": 50,
67 | "height": 70,
68 | "margin":-5
69 | },
70 | "caisseDeCommunaute":{
71 | "src": "img/starwars/jedi_light_saber.png",
72 | "width": 50,
73 | "height": 70,
74 | "margin":-5
75 | },
76 | "compagnie":{
77 | "src": "img/light.png",
78 | "width": 50,
79 | "height": 35,
80 | "margin": 5
81 | },
82 | "gare":{
83 | "src": "img/starwars/xwing.png",
84 | "width": 50,
85 | "height": 50
86 | }
87 | },
88 | "titles":{
89 | "chance":"Sith",
90 | "communaute":"Jedi"
91 | },
92 | "chance":{
93 | "cartes":[
94 | {"nom":"Payer le carburant du landspeeder 7 150","type":"taxe","montant":150},
95 | {"nom":"Amende pour excès de vitesse : 7 15","type":"taxe","montant":15},
96 | {"nom":"Vous avez gagné le prix de tir. Recevez 7 100","type":"prime","montant":100},
97 | {"nom":"La banque vous verse un dividende de 7 50","type":"prime","montant":50},
98 | {"nom":"Votre immeuble et votre prêt rapportent. Vous devez toucher 7 150","type":"prime","montant":150},
99 | {"nom":"Amende pour ivresse : 7 20","type":"taxe","montant":20},
100 | {"nom":"Rendez vous à Ord Mantell. Si vous passez par la case Départ, recevez 7 200","type":"goto","axe":0,"pos":4},
101 | {"nom":"Rendez vous au Coruscant Senate","type":"goto","axe":1,"pos":9},
102 | {"nom":"Reculez de 3 cases","type":"move","nb":-3},
103 | {"nom":"Avancez jusqu'à la case Départ","type":"goto","axe":2,"pos":0},
104 | {"nom":"Rendez vous sur l'Imperial Star Destroyer. Si vous passez par la case Départ, recevez 7 200","type":"goto","axe":3,"pos":5},
105 | {"nom":"Atterissez sur Hoth. Si vous passez par la case Départ, recevez 7 200","type":"goto","axe":3,"pos":1},
106 | {"nom":"Allez en prison. Rendez vous directement à la prison. Ne franchissez par la case Départ. Ne touchez pas 7 200","type":"goto","axe":1,"pos":0,"direct":true}
107 | ]
108 | },
109 | "communaute":{
110 | "cartes":[
111 | {"nom":"La vente de votre stock pour rapport 7 50","type":"prime","montant":50},
112 | {"nom":"Payez votre Police d'Assurance s'élevant à 7 50","type":"taxe","montant":50},
113 | {"nom":"Recevez votre revenu annuel 7 100","type":"prime","montant":100},
114 | {"nom":"Vous avez gagné le deuxième Prix de Beauté. Recevez 7 10","type":"prime","montant":10},
115 | {"nom":"Payez la note du Médecin 7 50","type":"taxe","montant":50},
116 | {"nom":"Les Contributions vous remboursent la somme de 7 20","type":"prime","montant":20},
117 | {"nom":"Erreur de la Banque en votre faveur Recevez 7 200","type":"prime","montant":200},
118 | {"nom":"Recevez votre intérêt sur l'emprunt à 7 % 7 25","type":"prime","montant":25},
119 | {"nom":"Payez à l'Hôpital 7 100","type":"taxe","montant":100},
120 | {"nom":"Vous héritez 7 100","type":"prime","montant":100},
121 | {"nom":"Placez vous sur la case Départ","type":"goto","axe":2,"pos":0},
122 | {"nom":"Allez en prison. Avancez tout droit en prison. Ne passez par la case Départ. Ne touchez pas 7 200","type":"goto","axe":1,"pos":0,"direct":true},
123 | {"nom":"Retournez à Tatooine Mos Eisley.","type":"goto","axe":2,"pos":1,"direct":true},
124 | {"nom":"Vous êtes libéré de prison","type":"prison"}
125 | ]
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/data/data-monopoly-original.json:
--------------------------------------------------------------------------------
1 | {
2 | "plateau":{
3 | "subtitle":"Original",
4 | "backgroundColor": "#A7E9DB",
5 | "textColor": "#000000",
6 | "nomsJoueurs":["Cat","Racecar","Top hat","Wheelbarrow","Thimble","Scottie Dog","Battleship","Boot"],
7 | "rollColor":"#999999",
8 | "depart":200
9 | },
10 | "background-image":"",
11 | "currency": "$",
12 | "fiches": [
13 | {"type": "depart","axe": 2,"pos": 0,"nom": "GO"},
14 | {"type": "propriete","groupe": "Violet","axe": 2,"pos": 1,"nom": "Mediterranean Avenue","colors": ["#812B5C", "#DFA4C6"],"prix": 60,"loyers": [2,10,30,90,160,250],"prixMaison": 50},
15 | {"type": "communaute","axe": 2,"pos": 2},
16 | {"type": "propriete","groupe": "Violet","axe": 2,"pos": 3,"nom": "Baltic Avenue","colors": ["#812B5C", "#DFA4C6"],"prix": 60,"loyers": [4, 20, 60, 180, 320, 450],"prixMaison": 50},
17 | {"type": "taxe","axe": 2,"pos": 4,"nom": "Impots sur le revenu","prix": 200},
18 | {"type": "gare","axe": 2,"pos": 5,"nom": "Reading Railroad","colors": ["#000000", "#ABABAB"],"prix": 200,"loyers": [25, 50, 100, 200]},
19 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 6,"nom": "Oriental Avenue","colors": ["#119AEB", "#98CEEE"],"prix": 100,"loyers": [6, 30, 90, 270, 400, 550],"prixMaison": 50},
20 | {"type": "chance","axe": 2,"pos": 7},
21 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 8,"nom": "Vermont Avenue","colors": ["#119AEB", "#98CEEE"],"prix": 100,"loyers": [6, 30, 90, 270, 400, 550],"prixMaison": 50},
22 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 9,"nom": "Connecticut Avenue","colors": ["#119AEB", "#98CEEE"],"prix": 120,"loyers": [8, 40, 100, 300, 450, 600],"prixMaison": 50},
23 | {"type": "special","axe": 3,"pos": 0,"nom": "Simple visite"},
24 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 1,"nom": "St. Charles Place","colors": ["#73316F", "#DFB4DC"],"prix": 140,"loyers": [10, 50, 150, 450, 625, 750],"prixMaison": 100},
25 | {"type": "compagnie","axe": 3,"pos": 2,"nom": "Electric Company","colors": ["lightgreen"],"prix": 150,"loyers": [400,1000]},
26 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 3,"nom": "States Avenue","colors": ["#73316F", "#DFB4DC"],"prix": 140,"loyers": [10, 50, 150, 450, 625, 750],"prixMaison": 100},
27 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 4,"nom": "Virginia Avenue","colors": ["#73316F", "#DFB4DC"],"prix": 160,"loyers": [12, 60, 180, 500, 700, 900],"prixMaison": 100},
28 | {"type": "gare","axe": 3,"pos": 5,"nom": "Pennsylvania Railroad","colors": ["#000000", "#ABABAB"],"prix": 200,"loyers": [25, 50, 100, 200]},
29 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 6,"nom": "St. James Place","colors": ["#D16E2D", "#FECC84"],"prix": 180,"loyers": [14, 70, 200, 550, 750, 950],"prixMaison": 100},
30 | {"type": "communaute","axe": 3,"pos": 7},
31 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 8,"nom": "Tennessee Avenue","colors": ["#D16E2D", "#FECC84"],"prix": 180,"loyers": [14, 70, 200, 550, 750, 950],"prixMaison": 100},
32 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 9,"nom": "New York Avenue","colors": ["#D16E2D", "#FECC84"],"prix": 200,"loyers": [16, 80, 220, 600, 800, 1000],"prixMaison": 100},
33 | {"type": "parc","axe": 0,"pos": 0,"nom": "Parc Gratuit"},
34 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 1,"nom": "Kentucky Avenue","colors": ["#D32C19", "#F9AEA6"],"prix": 220,"loyers": [18, 90, 250, 700, 875, 1050],"prixMaison": 150},
35 | {"type": "chance","axe": 0,"pos": 2},
36 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 3,"nom": "Indiana Avenue","colors": ["#D32C19", "#F9AEA6"],"prix": 220,"loyers": [18, 90, 250, 700, 875, 1050],"prixMaison": 150},
37 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 4,"nom": "Illinois Avenue","colors": ["#D32C19", "#F9AEA6"],"prix": 240,"loyers": [20, 100, 300, 750, 925, 1100],"prixMaison": 150},
38 | {"type": "gare","axe": 0,"pos": 5,"nom": "B&O Railroad","colors": ["#000000", "#ABABAB"],"prix": 200,"loyers": [25, 50, 100, 200]},
39 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 6,"nom": "Atlantic Avenue","colors": ["#E6E018", "#F8F587"],"prix": 260,"loyers": [22, 110, 330, 800, 975, 1150],"prixMaison": 150},
40 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 7,"nom": "Ventnor Avenue","colors": ["#E6E018", "#F8F587"],"prix": 260,"loyers": [22, 110, 330, 800, 975, 1150],"prixMaison": 150},
41 | {"type": "compagnie","axe": 0,"pos": 8,"nom": "Water Works","colors": ["lightgreen"],"prix": 150,"loyers": [400,1000]},
42 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 9,"nom": "Marvin Gardens","colors": ["#E6E018", "#F8F587"],"prix": 280,"loyers": [24, 120, 360, 850, 1025, 1200],"prixMaison": 150},
43 | {"type": "prison","axe": 1,"pos": 0,"nom": "Go To Jail"},
44 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 1,"nom": "Pacific Avenue","colors": ["#11862E", "#93D1A2"],"prix": 300,"loyers": [26, 130, 390, 900, 1100, 1275],"prixMaison": 200},
45 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 2,"nom": "North Carolina Avenue","colors": ["#11862E", "#93D1A2"],"prix": 300,"loyers": [26, 130, 390, 900, 1100, 1275],"prixMaison": 200},
46 | {"type": "communaute","axe": 1,"pos": 3},
47 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 4,"nom": "Pennsylvania Avenue","colors": ["#11862E", "#93D1A2"],"prix": 320,"loyers": [28, 150, 450, 1000, 1200, 1400],"prixMaison": 200},
48 | {"type": "gare","axe": 1,"pos": 5,"nom": "Short Line","colors": ["#000000", "#ABABAB"],"prix": 200,"loyers": [25, 50, 100, 200]},
49 | {"type": "chance","axe": 1,"pos": 6},
50 | {"type": "propriete","groupe": "Bleu","axe": 1,"pos": 7,"nom": "Park Place","colors": ["#132450", "#808EB0"],"prix": 350,"loyers": [35, 175, 500, 1100, 1300, 1500],"prixMaison": 200},
51 | {"type": "taxe","axe": 1,"pos": 8,"nom": "Luxury Tax","prix": 100},
52 | {"type": "propriete","groupe": "Bleu","axe": 1,"pos": 9,"nom": "Boardwalk","colors": ["#132450", "#808EB0"],"prix": 400,"loyers": [50, 200, 600, 1400, 1700, 2000],"prixMaison": 200}
53 | ],
54 | "images":{
55 | "taxe": {
56 | "src": "img/bijou.png",
57 | "width": 40,
58 | "height": 50
59 | },
60 | "chance":{
61 | "src": "img/interrogation.png",
62 | "width": 50,
63 | "height": 60
64 | },
65 | "caisseDeCommunaute":{
66 | "src": "img/banque2.png",
67 | "width": 60,
68 | "height": 60
69 | },
70 | "compagnie":{
71 | "src": "img/light2.png",
72 | "width": 40,
73 | "height": 40,
74 | "margin": 5
75 | },
76 | "gare":{
77 | "src": "img/big_train.png",
78 | "width": 50,
79 | "height": 35,
80 | "margin": 5
81 | }
82 | },
83 | "titles":{
84 | "chance":"Chance",
85 | "communaute":"Community Chest"
86 | },
87 | "chance":{
88 | "cartes":[
89 | {"nom":"You have been elected Chairman of the Board – Pay each player $50","type":"birthday","montant":50},
90 | {"nom":"Make general repairs on all your property – For each house pay $25 – For each hotel $100","type":"repair","hotel":100,"maison":25},
91 | {"nom":"Pay poor tax of $15","type":"taxe","montant":15},
92 | {"nom":"You have won a crossword competition - Collect $100","type":"prime","montant":100},
93 | {"nom":"Bank pays you dividend of $50","type":"prime","montant":50},
94 | {"nom":"Your building loan matures – Collect $150","type":"prime","montant":150},
95 | {"nom":"Advance to Illinois Ave. - If you pass Go, collect $200","type":"goto","axe":0,"pos":4},
96 | {"nom":"Take a trip to Reading Railroad ","type":"goto","axe":2,"pos":5},
97 | {"nom":"Take a walk on the Boardwalk","type":"goto","axe":1,"pos":9},
98 | {"nom":"Go Back 3 Spaces","type":"move","nb":-3},
99 | {"nom":"Advance to Go, collect $200 ","type":"goto","axe":2,"pos":0},
100 | {"nom":"Advance to St. Charles Place – If you pass Go, collect $200","type":"goto","axe":3,"pos":1},
101 | {"nom":"Go to Jail – Go directly to Jail – Do not pass Go, do not collect $200","type":"goto","axe":1,"pos":0,"direct":true},
102 | {"nom":"Get out of Jail Free – This card may be kept until needed, or traded/sold","type":"prison"}
103 | ]
104 | },
105 | "communaute":{
106 | "cartes":[
107 | {"nom":"From sale of stock you get $50","type":"prime","montant":50},
108 | {"nom":"Grand Opera Night – Collect $50 from every player for opening night seats","type":"birthday","montant":50},
109 | {"nom":"Life insurance matures – Collect $100","type":"prime","montant":100},
110 | {"nom":"Holiday Fund matures - Receive $100 ","type":"prime","montant":100},
111 | {"nom":"You have won second prize in a beauty contest – Collect $10","type":"prime","montant":10},
112 | {"nom":"Doctor's fees – Pay $50 ","type":"taxe","montant":50},
113 | {"nom":"It is your birthday - Collect $10 from each player","type":"birthday","montant":10},
114 | {"nom":"Income tax refund – Collect $20","type":"prime","montant":20},
115 | {"nom":"Bank error in your favor – Collect $200","type":"prime","montant":200},
116 | {"nom":"Receive $25 consultancy fee","type":"prime","montant":25},
117 | {"nom":"Pay hospital fees of $100","type":"taxe","montant":100},
118 | {"nom":"Pay school fees of $150 ","type":"taxe","montant":150},
119 | {"nom":"You inherit $100","type":"prime","montant":100},
120 | {"nom":"Advance to Go","type":"goto","axe":2,"pos":0},
121 | {"nom":"Go to Jail – Go directly to jail – Do not pass Go – Do not collect $200","type":"goto","axe":1,"pos":0,"direct":true},
122 | {"nom":"Get out of Jail Free – This card may be kept until needed, or traded/sold","type":"prison"},
123 | {"nom":"You are assessed for street repairs – $40 per house – $115 per hotel ","type":"repair","hotel":115,"maison":40}
124 | ]
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/js/ui/graphics.js:
--------------------------------------------------------------------------------
1 |
2 | /* Fournit les methodes de dessin d'un plateau de jeu standard (carré) */
3 |
4 | import {bus} from "../bus_message.js";
5 |
6 | let CURRENT_ID_COMPONENT = 0; // Permet de generer un numero de composant unique
7 |
8 | /* Fournit des methodes de dessins */
9 | let DrawerHelper = {
10 | fontWeight:200,
11 | setFontWeight(font){
12 | this.fontWeight = font;
13 | },
14 | fromDegresToRad:function(angle){
15 | return (angle/180)*Math.PI;
16 | },
17 | drawCircle:function(canvas,color,rayon,center,strokeColor=color){
18 | canvas.fillStyle=color;
19 | canvas.strokeColor = strokeColor;
20 | canvas.beginPath();
21 | canvas.arc(center.x,center.y,rayon,0,2*Math.PI);
22 | canvas.fill();
23 | canvas.closePath();
24 | },
25 | drawArcCircle:function(canvas,color,rayon,center,alphaStart,alphaEnd){
26 | canvas.beginPath();
27 | canvas.fillStyle = color;
28 | canvas.moveTo(center.x,center.y);
29 | canvas.arc(center.x,center.y,rayon,alphaStart,alphaEnd);
30 | canvas.fill();
31 | canvas.closePath();
32 | },
33 | drawImage: function (canvas, img, x, y, width, height, rotate) {
34 | canvas.save();
35 | canvas.translate(x, y);
36 | canvas.rotate(rotate);
37 | try{
38 | canvas.drawImage(img, 0, 0, width, height);
39 | }catch(e){}
40 | canvas.restore();
41 | },
42 | /* Detect les sauts de ligne */
43 | _splitLine:function(mots){
44 | if(mots.some(function(m){return m.indexOf('\n')!==-1;})){
45 | let mots2 = [];
46 | mots.forEach(function(m){
47 | if(m.indexOf('\n')!==-1){
48 | m.split('\n').forEach(function(mm){mots2.push(mm);});
49 | }
50 | else{
51 | mots2.push(m);
52 | }
53 | });
54 | mots = mots2;
55 | }
56 | return mots;
57 | },
58 | /* @param align : si null, center, sinon 'left' ou 'right' */
59 | writeText: function (text, x, y, rotate, canvas, size="7", width = DrawerFactory.dimensions.largeur, align) {
60 | canvas.fillStyle=DrawerFactory.getInfo('textColor') || '#000000';
61 | canvas.font = `${this.fontWeight} ` + size + "pt Arial";
62 |
63 | let mots = this._splitLine([text]);
64 |
65 | let mots2 = [];
66 | mots.forEach(function(m){
67 | if (canvas.measureText(m).width > width - 5) {
68 | // On split les mots intelligement (on regroupe)
69 | let splitMots = m.split(" ");
70 | let tempMots = [];
71 | let pos = 0;
72 | for (let i = 0; i < splitMots.length; i++) {
73 | if (pos > 0 && (canvas.measureText(tempMots[pos - 1]).width + canvas.measureText(splitMots[i]).width) < width - 5) {
74 | // on concatene
75 | tempMots[pos - 1] = tempMots[pos - 1] + " " + splitMots[i];
76 | } else {
77 | tempMots[pos++] = splitMots[i];
78 | }
79 | }
80 | // On ajoute les mots
81 | tempMots.forEach(function(m){mots2.push(m);});
82 | }else{
83 | mots2.push(m);
84 | }
85 | });
86 | mots = mots2;
87 |
88 | canvas.save();
89 | canvas.translate(x, y);
90 | canvas.rotate(rotate);
91 | const pas = 12;
92 | for (let i = 0; i < mots.length; i++) {
93 | let lng;
94 | switch(align){
95 | case 'left':lng=0;break;
96 | case 'right':lng = width - canvas.measureText(mots[i]).width;break;
97 | default : lng = (width - canvas.measureText(mots[i]).width) / 2;
98 | }
99 | canvas.fillText(mots[i], lng, i * pas);
100 | }
101 | canvas.font = "6pt Times news roman";
102 | canvas.restore();
103 | }
104 | }
105 |
106 | // Class which represent any component
107 | class Component{
108 | constructor(){
109 | this.id = CURRENT_ID_COMPONENT++;
110 | }
111 | draw(){
112 | throw "Not implemented";
113 | }
114 | }
115 |
116 | /* En fonction du type de plateau (square, circle), fournit les objets permettant de le construire */
117 | /* Renvoie uniquement des components (implemente draw) */
118 | let DrawerFactory = {
119 | instances:[],
120 | type:null,
121 | infos:{}, // Infos communes
122 | dimensions:{
123 | largeur:65,
124 | largeurPion:20,
125 | hauteur:100,
126 | bordure:40,
127 | plateauSize:800,
128 | innerPlateauSize:220,
129 | nbCases:10,
130 | textSize:7
131 | },
132 | /*setSize(size){
133 | $('#plateau').height(size+10).width(size+10);
134 | $('#canvas').width(size+10).height(size+10).attr('width',size+10).attr('height',size+10);
135 | $('#canvas_rt').width(size+10).height(size+10).attr('width',size+10).attr('height',size+10);
136 | this.dimensions.plateauSize = size;
137 | DrawerHelper.setFontWeight(800);
138 | this.computeDimensions();
139 | },*/
140 | setNbCases(nbCases = 10){
141 | this.dimensions.nbCases = nbCases;
142 | this.computeDimensions();
143 | },
144 | computeDimensions(){
145 | let targetWidth = 570;
146 | // Width of a case if computed for minimum size divide by nb inside cases
147 | this.dimensions.largeur = targetWidth/(this.dimensions.nbCases -1)+2;
148 | this.dimensions.hauteur = (this.dimensions.plateauSize - targetWidth -20)/2;
149 | this.dimensions.textSize = Math.round(this.dimensions.largeur/10);
150 | },
151 | init:function(){
152 | return this;
153 | },
154 | /* Configure la factory */
155 | setType:function(type){
156 | this.type = type;
157 | },
158 | addInfo:function(name,info){
159 | this.infos[name] = info;
160 | },
161 | getInfo:function(name){
162 | return this.infos[name];
163 | },
164 | /* Ajoute une nouvelle implementation */
165 | addInstance:function(instance){
166 | this.instances[instance.type] = instance;
167 | },
168 | getDes:function(x, y, width){
169 | return this._instantiate('des',arguments);
170 | },
171 | getDesRapide:function(x, y, width){
172 | return this._instantiate('desRapide',arguments);
173 | },
174 | getCase:function(pos,axe,color,nom,prix,img){
175 | return this._instantiate('standardCase',arguments);
176 | },
177 | getCaseSpeciale:function(axe,titre){
178 | return this._instantiate('specialCase',arguments);
179 | },
180 | getPionJoueur:function(color){
181 | return this._instantiate('pionJoueur',arguments);
182 | },
183 | getPlateau:function(x,y,width,height,color){
184 | return this._instantiate('plateau',arguments);
185 | },
186 | endPlateau:function(){
187 | return this._instantiate('endPlateau',arguments);
188 | },
189 | _instantiate:function(method,params){
190 | if(this.instances[this.type] == null){
191 | throw "Creation, type : " + this.type + " inconnu";
192 | }
193 | if(this.instances[this.type][method] == null){
194 | return null;
195 | }
196 | return new this.instances[this.type][method](...params);
197 | }
198 | }.init();
199 |
200 | // Gere les dessins et les calques
201 | let Drawer = {
202 | components: [], // Un ordre est ajoute lors de l'insertion
203 | height: 0,
204 | width: 0,
205 | interval: null,
206 | intervalRT: null,
207 | canvas: null,
208 | canvasRT: null, //Canvas de temps reel
209 | // ajoute un composant. On indique le canvas sur lequel il s'affiche
210 | intervals: [], // Stocke les flags d'arret du refresh
211 | /* @param order : Si present, indique l'ordre d'affichage. Le plus petit en premier */
212 | reset:function(){
213 | this.components = [];
214 | if(this.canvas) {
215 | this.clear(this.canvas);
216 | }
217 | if(this.canvasRT) {
218 | this.clear(this.canvasRT);
219 | }
220 | },
221 | add: function (component, order) {
222 | if(component == null){return;}
223 | component.getId = function () {
224 | return Drawer.canvas.canvas.id
225 | };
226 | component.order = (order==null)?0:order;
227 | Drawer.components.push(component);
228 | },
229 | addRealTime: function (component) {
230 | component.getId = function () {
231 | return Drawer.canvasRT.canvas.id
232 | };
233 | Drawer.components.push(component);
234 | },
235 | removeComponent: function (component) {
236 | // Boucle sur les composants et supprime si l'id est le meme
237 | for (let i = 0; i < this.components.length; i++) {
238 | if (this.components[i].id === component.id) {
239 | this.components.splice(i, 1);
240 | return;
241 | }
242 | }
243 | },
244 | clear: function (canvas) {
245 | canvas.clearRect(0, 0, this.width, this.height);
246 | },
247 | /* Rafraichit un seul canvas */
248 | refresh: function (canvas) {
249 | Drawer.clear(canvas);
250 | for (let i = 0; i < Drawer.components.length; i++) {
251 | if (Drawer.components[i].getId() === canvas.canvas.id) {
252 | Drawer.components[i].draw(canvas);
253 | }
254 | }
255 | },
256 | // Refraichissement du graphique, time en ms
257 | setFrequency: function (time, canvas) {
258 | if (Drawer.intervals[canvas.canvas.id] != null) {
259 | clearInterval(Drawer.intervals[canvas.canvas.id]);
260 | }
261 | Drawer.intervals[canvas.canvas.id] = setInterval(function () {
262 | Drawer.refresh(canvas);
263 | }, time);
264 | },
265 | init: function (width, height) {
266 | if(document.getElementById('canvas') == null){
267 | return;
268 | }
269 | // On tri les composants qui ont ete ajoutes
270 | this.components.sort(function(a,b){
271 | return a.order - b.order;
272 | });
273 | this.width = width;
274 | this.height = height;
275 | this.canvas = document.getElementById("canvas").getContext("2d");
276 | this.canvasRT = document.getElementById("canvas_rt").getContext("2d");
277 | this.canvas.strokeStyle = '#AA0000';
278 | this.canvasRT.strokeStyle = '#AA0000';
279 | // On ne recharge pas le plateau, il n'est charge qu'une seule fois (ou rechargement a la main)
280 | this.refresh(this.canvas);
281 | this.setFrequency(50, this.canvasRT);
282 |
283 | bus.watchRefresh(()=>Drawer.refresh(Drawer.canvas));
284 | return this;
285 | }
286 | };
287 |
288 | export {DrawerFactory,Drawer,Component,DrawerHelper};
289 |
--------------------------------------------------------------------------------
/data/data-monopoly.json:
--------------------------------------------------------------------------------
1 | {
2 | "plateau":{
3 | "subtitle":"Classique",
4 | "backgroundColor": "#cee6d0",
5 | "textColor": "#000000",
6 | "nomsJoueurs":["Chat","Voiture","Chapeau","Brouette","Dés à Coudre","Chien","Navire"],
7 | "rollColor":"#999999",
8 | "nbCases": 10
9 | },
10 | "background-image":"",
11 | "currency": "Fr.",
12 | "fiches": [
13 | {"type": "depart","axe": 2,"pos": 0,"nom": "Départ"},
14 | {"type": "propriete","groupe": "Violet","axe": 2,"pos": 1,"nom": "Boulevard de Belleville","colors": ["#812B5C", "#DFA4C6"],"prix": 4000,"loyers": [200,1000,3000,9000,16000,25000],"prixMaison": 5000},
15 | {"type": "communaute","axe": 2,"pos": 2},
16 | {"type": "propriete","groupe": "Violet","axe": 2,"pos": 3,"nom": "Rue Lecourbe","colors": ["#812B5C", "#DFA4C6"],"prix": 8000,"loyers": [400, 2000, 6000, 18000, 32000, 45000],"prixMaison": 5000},
17 | {"type": "taxe","axe": 2,"pos": 4,"nom": "Impots sur le revenu","prix": 20000},
18 | {"type": "gare","axe": 2,"pos": 5,"nom": "Gare Montparnasse","colors": ["#000000", "#ABABAB"],"prix": 20000,"loyers": [2500, 5000, 10000, 20000]},
19 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 6,"nom": "Rue de Vaugirard","colors": ["#119AEB", "#98CEEE"],"prix": 10000,"loyers": [600, 3000, 9000, 27000, 40000, 55000],"prixMaison": 5000},
20 | {"type": "chance","axe": 2,"pos": 7},
21 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 8,"nom": "Rue de Courcelles","colors": ["#119AEB", "#98CEEE"],"prix": 10000,"loyers": [600, 3000, 9000, 27000, 40000, 55000],"prixMaison": 5000},
22 | {"type": "propriete","groupe": "Bleu clair","axe": 2,"pos": 9,"nom": "Avenue de la Republique","colors": ["#119AEB", "#98CEEE"],"prix": 12000,"loyers": [800, 4000, 10000, 30000, 45000, 60000],"prixMaison": 5000},
23 | {"type": "special","axe": 3,"pos": 0,"nom": "Simple visite"},
24 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 1,"nom": "Boulevard de la Villette","colors": ["#73316F", "#DFB4DC"],"prix": 14000,"loyers": [1000, 5000, 15000, 45000, 62500, 75000],"prixMaison": 10000},
25 | {"type": "compagnie","axe": 3,"pos": 2,"nom": "Compagnie de distribution d'électricité","colors": ["lightgreen"],"prix": 15000,"loyers": [400,1000],"img": "light"},
26 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 3,"nom": "Avenue de Neuilly","colors": ["#73316F", "#DFB4DC"],"prix": 14000,"loyers": [1000, 5000, 15000, 45000, 62500, 75000],"prixMaison": 10000},
27 | {"type": "propriete","groupe": "Violet clair","axe": 3,"pos": 4,"nom": "Rue de Paradis","colors": ["#73316F", "#DFB4DC"],"prix": 16000,"loyers": [1200, 6000, 18000, 50000, 70000, 90000],"prixMaison": 10000},
28 | {"type": "gare","axe": 3,"pos": 5,"nom": "Gare de Lyon","colors": ["#000000", "#ABABAB"],"prix": 20000,"loyers": [2500, 5000, 10000, 20000]},
29 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 6,"nom": "Avenue Mozart","colors": ["#D16E2D", "#FECC84"],"prix": 18000,"loyers": [1400, 7000, 20000, 55000, 75000, 95000],"prixMaison": 10000},
30 | {"type": "communaute","axe": 3,"pos": 7},
31 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 8,"nom": "Boulevard Saint-Michel","colors": ["#D16E2D", "#FECC84"],"prix": 18000,"loyers": [1400, 7000, 20000, 55000, 75000, 95000],"prixMaison": 10000},
32 | {"type": "propriete","groupe": "Orange","axe": 3,"pos": 9,"nom": "Place Pigalle","colors": ["#D16E2D", "#FECC84"],"prix": 20000,"loyers": [1600, 8000, 22000, 60000, 80000, 100000],"prixMaison": 10000},
33 | {"type": "parc","axe": 0,"pos": 0,"nom": "Parc Gratuit"},
34 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 1,"nom": "Avenue Matignon","colors": ["#D32C19", "#F9AEA6"],"prix": 22000,"loyers": [1800, 9000, 25000, 70000, 87500, 105000],"prixMaison": 15000},
35 | {"type": "chance","axe": 0,"pos": 2},
36 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 3,"nom": "Boulevard Malsherbes","colors": ["#D32C19", "#F9AEA6"],"prix": 22000,"loyers": [1800, 9000, 25000, 70000, 87500, 105000],"prixMaison": 15000},
37 | {"type": "propriete","groupe": "Rouge","axe": 0,"pos": 4,"nom": "Avenue Henri-Martin","colors": ["#D32C19", "#F9AEA6"],"prix": 24000,"loyers": [2000, 10000, 30000, 75000, 92500, 110000],"prixMaison": 15000},
38 | {"type": "gare","axe": 0,"pos": 5,"nom": "Gare du Nord","colors": ["#000000", "#ABABAB"],"prix": 20000,"loyers": [2500, 5000, 10000, 20000]},
39 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 6,"nom": "Boulevard Saint-Honoré","colors": ["#E6E018", "#F8F587"],"prix": 26000,"loyers": [2200, 11000, 33000, 80000, 97500, 115000],"prixMaison": 15000},
40 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 7,"nom": "Place de la Bourse","colors": ["#E6E018", "#F8F587"],"prix": 26000,"loyers": [2200, 11000, 33000, 80000, 97500, 115000],"prixMaison": 15000},
41 | {"type": "compagnie","axe": 0,"pos": 8,"nom": "Compagnie de distribution des eaux","colors": ["lightgreen"],"prix": 15000,"loyers": [400,1000],"img": "water"},
42 | {"type": "propriete","groupe": "Jaune","axe": 0,"pos": 9,"nom": "Rue Lafayette","colors": ["#E6E018", "#F8F587"],"prix": 28000,"loyers": [2400, 12000, 36000, 85000, 102500, 120000],"prixMaison": 15000},
43 | {"type": "prison","axe": 1,"pos": 0,"nom": "Allez en prison"},
44 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 1,"nom": "Avenue de Breteuil","colors": ["#11862E", "#93D1A2"],"prix": 30000,"loyers": [2600, 13000, 39000, 90000, 110000, 127500],"prixMaison": 20000},
45 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 2,"nom": "Avenue Foch","colors": ["#11862E", "#93D1A2"],"prix": 30000,"loyers": [2600, 13000, 39000, 90000, 110000, 127500],"prixMaison": 20000},
46 | {"type": "communaute","axe": 1,"pos": 3},
47 | {"type": "propriete","groupe": "Vert","axe": 1,"pos": 4,"nom": "Boulevard des Capucines","colors": ["#11862E", "#93D1A2"],"prix": 32000,"loyers": [2800, 15000, 45000, 100000, 120000, 140000],"prixMaison": 20000},
48 | {"type": "gare","axe": 1,"pos": 5,"nom": "Gare Saint-Lazarre","colors": ["#000000", "#ABABAB"],"prix": 20000,"loyers": [2500, 5000, 10000, 20000]},
49 | {"type": "chance","axe": 1,"pos": 6},
50 | {"type": "propriete","groupe": "Bleu","axe": 1,"pos": 7,"nom": "Avenue des Champs Elysées","colors": ["#132450", "#808EB0"],"prix": 35000,"loyers": [3500, 17500, 50000, 110000, 130000, 150000],"prixMaison": 20000},
51 | {"type": "taxe","axe": 1,"pos": 8,"nom": "Taxe de luxe","prix": 10000},
52 | {"type": "propriete","groupe": "Bleu","axe": 1,"pos": 9,"nom": "Rue de la Paix","colors": ["#132450", "#808EB0"],"prix": 40000,"loyers": [5000, 20000, 60000, 140000, 170000, 200000],"prixMaison": 20000}
53 | ],
54 | "images":{
55 | "taxe": {
56 | "src": "img/bijou.png",
57 | "width": 40,
58 | "height": 50
59 | },
60 | "chance":{
61 | "src": "img/interrogation.png",
62 | "width": 50,
63 | "height": 60
64 | },
65 | "caisseDeCommunaute":{
66 | "src": "img/banque2.png",
67 | "width": 60,
68 | "height": 60
69 | },
70 | "compagnie":{
71 | "src": "img/light2.png",
72 | "width": 28,
73 | "height": 28,
74 | "margin": 17
75 | },
76 | "water":{
77 | "src": "img/tap.png",
78 | "width": 28,
79 | "height": 28,
80 | "margin": 17
81 | },
82 | "light":{
83 | "src": "img/light2.png",
84 | "width": 28,
85 | "height": 28,
86 | "margin": 17
87 | },
88 | "gare":{
89 | "src": "img/big_train.png",
90 | "width": 50,
91 | "height": 35,
92 | "margin": 5
93 | }
94 | },
95 | "titles":{
96 | "chance":"Chance",
97 | "communaute":"Caisse de Communauté"
98 | },
99 | "chance":{
100 | "cartes":[
101 | {"nom":"C'est votre anniversaire, chaque joueur vous donne F 1.000","type":"birthday","montant":1000},
102 | {"nom":"Faites des réparations dans toutes vos maisons : F 11.500 par hôtel et F 4.500 par maison","type":"repair","hotel":11500,"maison":4500},
103 | {"nom":"Payer pour frais de scolarité F 15.000","type":"taxe","montant":15000},
104 | {"nom":"Amende pour excès de vitesse : F 1.500","type":"taxe","montant":1500},
105 | {"nom":"Vous avez gagné le prix de mots croisés. Recevez F 10.000","type":"prime","montant":10000},
106 | {"nom":"La banque vous verse un dividende de F 5.000","type":"prime","montant":5000},
107 | {"nom":"Votre immeuble et votre prêt rapportent. Vous devez toucher F 15.000","type":"prime","montant":15000},
108 | {"nom":"Amende pour ivresse : F 2.000","type":"taxe","montant":2000},
109 | {"nom":"Rendez vous à l'avenue Henri-Martin. Si vous passez par la case Départ, recevez F 20.000","type":"goto","axe":0,"pos":4},
110 | {"nom":"Rendez vous à la Rue de la Paix","type":"goto","axe":1,"pos":9},
111 | {"nom":"Reculez de 3 cases","type":"move","nb":-3,"primeDepart": false,"direct": true},
112 | {"nom":"Avancez jusqu'à la case Départ","type":"goto","axe":2,"pos":0},
113 | {"nom":"Rendez vous à la gare de Lyon. Si vous passez par la case Départ, recevez F 20.000","type":"goto","axe":3,"pos":5},
114 | {"nom":"Avancez au Boulevard de la Villette. Si vous passez par la case Départ, recevez F 20.000","type":"goto","axe":3,"pos":1},
115 | {"nom":"Allez en prison. Rendez vous directement à la prison. Ne franchissez par la case Départ. Ne touchez pas F 20.000","type":"goto","axe":1,"pos":0,"direct":true}
116 | ]
117 | },
118 | "communaute":{
119 | "cartes":[
120 | {"nom":"La vente de votre stock pour rapport F 5.000","type":"prime","montant":5000},
121 | {"nom":"Payez votre Police d'Assurance s'élevant à F 5.000","type":"taxe","montant":5000},
122 | {"nom":"Recevez votre revenu annuel F 10.000","type":"prime","montant":10000},
123 | {"nom":"Vous avez gagné le deuxième Prix de Beauté. Recevez F 1.000","type":"prime","montant":1000},
124 | {"nom":"Payez la note du Médecin F 5.000","type":"taxe","montant":5000},
125 | {"nom":"Les Contributions vous remboursent la somme de F 2.000","type":"prime","montant":2000},
126 | {"nom":"Erreur de la Banque en votre faveur Recevez F 20.000","type":"prime","montant":20000},
127 | {"nom":"Recevez votre intérêt sur l'emprunt à 7 % F 2.500","type":"prime","montant":2500},
128 | {"nom":"Payez à l'Hôpital F 10.000","type":"taxe","montant":10000},
129 | {"nom":"Vous héritez F 10.000","type":"prime","montant":10000},
130 | {"nom":"Placez vous sur la case Départ","type":"goto","axe":2,"pos":0},
131 | {"nom":"Allez en prison. Avancez tout droit en prison. Ne passez par la case Départ. Ne touchez pas F 20.000","type":"goto","axe":1,"pos":0,"direct":true},
132 | {"nom":"Retournez à Belleville.","type":"goto","axe":2,"pos":1,"direct":true,"primeDepart": false},
133 | {"nom":"Vous êtes libéré de prison","type":"prison"}
134 | ]
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/js/entity/strategie.js:
--------------------------------------------------------------------------------
1 | /* Gestion de la strategie */
2 | import {GestionFiche} from "../display/case_jeu.js";
3 | import {ETAT_LIBRE} from "../display/case_jeu.js";
4 | import {GestionJoueur} from "../core/gestion_joueurs.js";
5 |
6 | /* @Abstract */
7 | /* Objet qui gere la strategie. IL y a differentes implementations */
8 | /* @colors : liste des groupes qui interessent le joueur */
9 | /* @param agressif : plus il est eleve, plus le joueur fait de l'antijeu (achat des terrains recherches par les adversaires) */
10 | class Strategie {
11 | constructor(colors, agressif, name, id, interetGare=(Math.round(Math.random() * 1000) % 3 === 0)) {
12 | this.groups = colors;
13 | this.agressif = agressif;
14 | this.interetGare = interetGare; // Interet pour gare
15 | this.name = name;
16 | this.id = id;
17 | }
18 |
19 | _containGroup(value) {
20 | for (let val in this.groups) {
21 | if (this.groups[val] === value) {
22 | return true;
23 | }
24 | }
25 | return false;
26 | }
27 |
28 | /* Renvoie des stats sur les proprietes concernees par cette strategie : nombre de propriete, nombre de libre... */
29 | getStatsProprietes(){
30 | let stats = {
31 | color: {
32 | total: 0,
33 | libre: 0,
34 | achete: 0,
35 | pourcent: 0
36 | },
37 | all: {
38 | total: 0,
39 | libre: 0,
40 | achete: 0,
41 | pourcent: 0
42 | }
43 | };
44 | let it = GestionFiche.iteratorTerrains();
45 | while (it.hasNext()) {
46 | let fiche = it.next();
47 | if (fiche.statut != null) {
48 | stats.all.total++;
49 | if (fiche.statut === ETAT_LIBRE) {
50 | stats.all.libre++;
51 | } else {
52 | stats.all.achete++;
53 | }
54 | if (this._containGroup(fiche.color)) {
55 | stats.color.total++;
56 | if (fiche.statut === ETAT_LIBRE) {
57 | stats.color.libre++;
58 | } else {
59 | stats.color.achete++;
60 | }
61 | }
62 | }
63 | }
64 | stats.color.pourcent = (stats.color.libre / stats.color.total) * 100;
65 | stats.all.pourcent = (stats.all.libre / stats.all.total) * 100;
66 | return stats;
67 | }
68 |
69 | /* Calcul l'interet global du joueur pour une propriete */
70 | /* Prend en compte l'interet propre (liste d'achat) ainsi que l'etat du groupe */
71 | /* Si ce n'est pas un terrain (gare, compagnie), la valeur est plus faible */
72 | /* @param isEnchere : indique que la mesure est faite pour une enchere */
73 | /* Cas des encheres, on ajoute un critere qui determine si le terrain est indispensable pour la strategie : autre groupe, autre terrain... */
74 | interetGlobal(propriete, joueur, isEnchere) {
75 | let i1 = this.interetPropriete(propriete);
76 | let statutGroup = this.statutGroup(propriete, joueur, isEnchere);
77 | let i2 = statutGroup.statut;
78 | let interet = {interet:1};
79 | if (i1 === false && i2 === 0) {
80 | interet = {interet:0.2}; // Permet en cas de situation tres confortable de continuer a investir
81 | }
82 | // Realise un blocage
83 | if (i1 === false && i2 === 2) {
84 | interet = {interet:this.agressif,joueur:statutGroup.joueur};
85 | }
86 | if (i1 === true && i2 === 3) {
87 | interet = {interet:4};
88 | }
89 | // Pas dans la strategie mais permet de completer le groupe et de construire
90 | if (i1 === false && i2 === 3) {
91 | interet = {interet:2};
92 | }
93 | // En possede deja
94 | if(i1 === true && i2 === 5){
95 | interet = {interet:1.5};
96 | }
97 | if(isEnchere){
98 | interet = {interet:this.interetProprieteInStrategie(propriete)};
99 | }
100 | if(!propriete.isTerrain()){
101 | interet.interet/=2;
102 | }
103 |
104 | return interet;
105 | }
106 |
107 | /* Determine l'interet de la propriete par rapport a l'etat de la strategie */
108 | /* @return : un nombre inférieur à 1 */
109 | interetProprieteInStrategie (){
110 | let stats = this.getStatsProprietes();
111 | /* Si les terrains sont presques tous libres, renvoie un calcul logarithmique (entre 67 et 100% de terrain libre) */
112 | return 0.5 + parseFloat((Math.log(((Math.min(100-stats.color.pourcent,32.5))/50)+1)).toFixed(2));
113 | }
114 |
115 | /* Calcul l'interet pour la maison (a partir des groupes interessant) */
116 | interetPropriete (propriete) {
117 | for (let color in this.groups) {
118 | if (this.groups[color] === propriete.color || (propriete.type === 'gare' && this.interetGare)) {
119 | return true;
120 | }
121 | }
122 | return false;
123 | }
124 |
125 | /* Renvoie le statut de la famille :
126 | 0 : toutes les proprietes sont libres
127 | 1 : s'il reste des libres apres celle ci
128 | 2 : si toutes appartiennent a une meme personne sauf celle-ci
129 | 3 : si toutes appartiennent au joueur sauf celle-ci
130 | 4 : autres
131 | 5 : joueur en possede deja une de la famille */
132 | /* @param isEnchere : achat du terrain a un autre joueur, on ne prend pas en compte le statut libre */
133 | statutGroup (propriete, joueur, isEnchere) {
134 | let nbTotal = 0;
135 | let nbLibre = 0;
136 | let dernierJoueur = null;
137 | let nbEquals = 0;
138 | let nbPossede = 0;
139 | for (let id in propriete.groupe.fiches) {
140 | let fiche = propriete.groupe.fiches[id];
141 | nbTotal++;
142 | if (fiche.statut === ETAT_LIBRE) {
143 | nbLibre++;
144 | } else {
145 | if (fiche.joueurPossede.equals(joueur)) {
146 | nbPossede++;
147 | } else {
148 | if (dernierJoueur == null || fiche.joueurPossede.equals(dernierJoueur)) {
149 | nbEquals++;
150 | }
151 | }
152 | dernierJoueur = fiche.joueurPossede;
153 | }
154 | }
155 | if (nbLibre === nbTotal) {
156 | return {statut:0};
157 | }
158 |
159 | if (nbLibre === 1 && nbEquals === nbTotal - 1) {
160 | return {statut:2,joueur:dernierJoueur};
161 | }
162 | /* Cas ou seul terrain manquant */
163 | if ((nbLibre === 1 || isEnchere) && nbPossede === nbTotal - 1) {
164 | return {statut:3};
165 | }
166 | /* Cas ou on en possede deja 1 */
167 | if (nbPossede > 0 && nbLibre > 0) {
168 | return {statut:5};
169 | }
170 | if (nbLibre > 0) {
171 | return {statut:1};
172 | }
173 | return {statut:4};
174 | }
175 |
176 | /* Calcule le fait d'accepter un terrain d'un joueur.
177 | * Se base sur le fait que le joueur a un deja un groupe, qu'il n'en a aucun.
178 | * Renvoie un facteur jouant sur le calcul final. 0 est bloquant, 1 est neutre...
179 | * @param otherInteresets : autres terrains qui interesent le joueur
180 | * @param interestGroupe : indique que le groupe interesse aussi le joueur
181 | */
182 | acceptSwapTerrain (terrain, joueur, otherInterests, interestGroupe) {
183 | /* Calcule si le proprio est le seul fournisseur */
184 | let alone = GestionJoueur.getNb() > 2; // Faux si seulement 2 joueurs
185 | /* Seul groupe qui m'interesse, on refuse */
186 | if ((interestGroupe === true && otherInterests.length === 1) || terrain.isGrouped()) {
187 | return 0;
188 | }
189 | for (let idx in otherInterests) {
190 | if (!otherInterests[idx].maison.joueurPossede.equals(terrain.joueurPossede)) {
191 | alone = false;
192 | }
193 | }
194 | let nbGroups = joueur.maisons.findConstructiblesGroupes().size();
195 | /* Le proprio est le seul a pouvoir aider le demandeur et il n'a pas encore de groupe */
196 | if (nbGroups === 0 && otherInterests.length === 0 && alone) {
197 | return this.agressif === 2 ? 0.5 : 1;
198 | }
199 | /* Beaucoup de groupe et seul fournisseur, on bloque si on est vicieux, on monte sinon */
200 | if (nbGroups >= 2 && alone) {
201 | return this.agressif > 0 ? 0 : 0.5;
202 | }
203 |
204 | /* Personne n'a de groupe et pas seul fournisseur */
205 | if (nbGroups === 0) {
206 | return 1.5;
207 | }
208 |
209 | /* Beaucoup de groupe mais pas le seul fournisseur, on ne bloque pas */
210 | if (nbGroups >= 2) {
211 | return this.agressif > 0 ? 0.5 : 1;
212 | }
213 | return 1;
214 | }
215 |
216 | toString(){
217 | return this.name + ((this.interetGare)?'
':'');
218 | }
219 | }
220 |
221 | /* Achete en priorite les terrains les moins chers : bleu marine-812B5C, bleu clair-119AEB, violet-73316F et orange-D16E2D */
222 | class CheapStrategie extends Strategie {
223 | constructor() {
224 | super(["#812B5C", "#119AEB", "#73316F", "#D16E2D"], 0, "Econome", 0);
225 | }
226 | }
227 |
228 | /* Achete en priorite les terrains moyennement chers : violet-73316F, orange-D16E2D, rouge-D32C19 et jaune-E6E018 */
229 | class MediumStrategie extends Strategie {
230 | constructor() {
231 | super(["#73316F", "#D16E2D", "#D32C19", "#E6E018"], 1, "Normal", 1);
232 | }
233 | }
234 |
235 | /* Achete en priorite les terrains les plus chers : rouge-D32C19, jaune-E6E018, vert-11862E et bleu fonce-132450 */
236 | class HardStrategie extends Strategie {
237 | constructor() {
238 | super(["#D32C19", "#E6E018", "#11862E", "#132450"], 2, "Luxe", 2);
239 | }
240 | }
241 |
242 | /* Achete en priorite les terrains les meilleurs (gare, orange-D16E2D, rouge-D32C19, jaune-E6E018) */
243 | class SmartStrategie extends Strategie {
244 | constructor() {
245 | super(["#D16E2D", "#D32C19", "#E6E018"], 2, "Futé", 3, true);
246 | }
247 | }
248 |
249 | /* Achete tout */
250 | class CrazyStrategie extends Strategie {
251 | constructor() {
252 | super(["#812B5C", "#119AEB", "#73316F", "#D16E2D", "#D32C19", "#E6E018", "#11862E", "#132450"], 4, "Dingue", 3, true);
253 | }
254 | }
255 |
256 | let GestionStrategie = {
257 | strategies : [CheapStrategie, MediumStrategie, HardStrategie,SmartStrategie,CrazyStrategie],
258 | create:function(id){
259 | return new this.strategies[id]();
260 | },
261 | createRandom:function(){
262 | return this.create(Math.round(Math.random() * 1000)%this.strategies.length);
263 | },
264 | getAll:function(){
265 | return this.strategies;
266 | },
267 | length:function(){
268 | return this.strategies.length;
269 | }
270 | };
271 |
272 | export {GestionStrategie};
--------------------------------------------------------------------------------
/js/core/gestion_joueurs.js:
--------------------------------------------------------------------------------
1 | import {Joueur,NetworkJoueur} from '../entity/joueur.js'
2 | import {RemotePlayer,MasterRemotePlayer,LocalPlayer} from '../entity/network/local_joueur.js'
3 | import {JoueurOrdinateur,NetworkJoueurOrdinateur} from "../entity/joueur_robot.js";
4 | import {CURRENCY,VARIANTES,globalStats,restartMonopoly} from "./monopoly.js"
5 | import {GestionFiche} from "../display/case_jeu.js";
6 | import {GestionEchange} from "./enchere.js";
7 | import {GestionDes} from "../entity/dices.js";
8 | import {infoMessage} from "../display/message.js";
9 | import {bus} from "../bus_message.js";
10 | import {dialog} from "../display/displayers.js";
11 | import {disableActions, enableActions} from "../utils.js";
12 |
13 | /* Gere les joueurs : creation, changement... */
14 | let GestionJoueur = {
15 | colorsJoueurs : ["#383C89", "#A6193E", "#C58F01", "#086B3D", "#B9B29B","#663300"],
16 | imgJoueurs : [],
17 | joueurs:[],
18 | joueurCourant:null,
19 | getJoueurCourant(){
20 | return this.joueurCourant;
21 | },
22 | setColors(colors){
23 | if(colors !=null){
24 | this.colorsJoueurs = colors;
25 | }
26 | },
27 | setImgJoueurs(img){
28 | if(img != null){
29 | this.imgJoueurs = img;
30 | }
31 | },
32 | getNbJoueurs(){
33 | return this.joueurs.length;
34 | },
35 | // renvoi le nombre de joueur dans la partie
36 | getNb(){
37 | return this.joueurs.reduce((somme,j)=>somme+(!j.defaite?1:0),0);
38 | },
39 | createAndLoad(isRobot,i,nom,data,montantDepart){
40 | let clazzPlayer = isRobot ? JoueurFactory.getRobotPlayer():JoueurFactory.getCurrentPlayer()
41 | return this.create(clazzPlayer,i,nom,data.defaite,0,montantDepart).saver.load(data);
42 | },
43 | init(){
44 | document.querySelectorAll('.panneau_joueur').forEach(e=>e.innerHTML = '');
45 | this.joueurs = [];
46 | this.joueurCourant = null;
47 | },
48 | lancerDes(){
49 | this.joueurCourant.lancerDes();
50 | },
51 | displayLineByGroupsAsElement(){
52 | let groups = GestionFiche.getGroups();
53 | const sub = (groups.size()+1) * 5;
54 | const size = `calc((45vw - ${sub}px) / ${groups.size()+1})`;
55 | let sizeBlock = 35 / groups.size();
56 | let div = document.createElement('div');
57 | div.classList.add('count-property');
58 | for(let name in groups){
59 | let color = name.replace(/ /g,"");
60 | div.insertAdjacentHTML('beforeend',`0`);
61 | }
62 | return div;
63 | },
64 | create(clazz, i, nom,defaite, argentDepart, montantDepart){
65 | let id = `joueur${i}`;
66 | let color = this.colorsJoueurs[i];
67 | let img = this.imgJoueurs[i];
68 | let joueur = new clazz(i, nom, color,argentDepart,montantDepart);
69 | joueur.enPrison = false;
70 | joueur.defaite = defaite;
71 | joueur.setEnableMouseFunction(JoueurFactory.mouseFunction);
72 | const parent = document.querySelector(`.player_${i%2===0?'left':'right'}`)
73 | const div = document.createElement('div');
74 | div.id = id;
75 | if(defaite){
76 | div.classList.add('defaite');
77 | }
78 |
79 | div.insertAdjacentHTML('beforeend',`${joueur.nom} : ${CURRENCY}
`)
80 | div.querySelectorAll('div.joueur-bloc').forEach(d=>d.style.setProperty('background-image', `linear-gradient(to right,white 50%,${color})`));
81 | div.append(this.displayLineByGroupsAsElement());
82 |
83 | parent.insertAdjacentHTML('beforeend','
');
84 |
85 | parent.append(div);
86 | joueur.setDiv(div);
87 | joueur.setPion(color,img,montantDepart);
88 |
89 | bus.send('monopoly.newPlayer', {
90 | joueur: joueur
91 | });
92 | this.joueurs.push(joueur);
93 | return joueur;
94 | },
95 | // Search by numero or playerID
96 | getByUniqueId(id){
97 | return this.joueurs.find(j=>j.uniqueID != null && j.uniqueID === id);
98 | },
99 | getById(id){
100 | return this.joueurs.find(j=>j.numero ===parseInt(id) || j.playerID === id);
101 | },
102 | /* @param idInitialJoueur : si present, joueur qui debute */
103 | change(idInitialJoueur){
104 | if(this.joueurCourant != null){
105 | this.joueurCourant.endTurn();
106 | }
107 | if(idInitialJoueur!=null){
108 | let joueur = this.getById(idInitialJoueur);
109 | if(joueur!=null){
110 | joueur.notifySelect();
111 | return this._select(joueur);
112 | }
113 | }
114 | // Si un echange est en cours, on ne change pas de joueur
115 | if (GestionEchange.running) {
116 | return;
117 | }
118 | // Joueur bloque, on le debloque avant de continuer
119 | let joueur = null;
120 | try {
121 | joueur = this.next();
122 | if(joueur != null && (GestionJoueur.getJoueurCourant() == null || joueur.id !== GestionJoueur.getJoueurCourant().id)) {
123 | bus.debug({
124 | message: `Change player to ${joueur.nom}`
125 | });
126 | }
127 | } catch (gagnant) {
128 | this._showVainqueur(gagnant);
129 | return null;
130 | }
131 | if (joueur == null) {
132 | return null;
133 | }
134 | if(GestionJoueur.getJoueurCourant() == null || joueur.id !== GestionJoueur.getJoueurCourant().id){
135 | joueur.notifySelect();
136 | GestionDes.resetDouble();
137 | }
138 | this._select(joueur);
139 | },
140 | // Renvoie la liste des joueurs disponibles
141 | getAvailableSlots(){
142 | return this.joueurs.filter(j=>j.isSlotFree());
143 | },
144 | _showVainqueur(gagnant){
145 | bus.send('monopoly.victoire',{joueur:gagnant});
146 | // On affiche les resultats complets
147 | const perdants = this.joueurs.filter(function(j){return j.defaite;});
148 | perdants.sort((a,b)=>{
149 | if(a.tourDefaite === b.tourDefaite){
150 | return b.numero - a.numero;
151 | }
152 | return b.tourDefaite - a.tourDefaite;
153 | });
154 | let message = `Le joueur ${gagnant.nom} a gagné en ${this._formatTempsJeu(globalStats.heureDebut)}.
`;
155 | message+=`1 - ${gagnant.nom}
`;
156 |
157 | message+= perdants.map((a,i)=>`${(i+2)} - ${a.nom}`).join("
")
158 | let score = this._calculateScore(gagnant);
159 | infoMessage.create(this.joueurCourant,`Fin de partie : ${score} Points`, "green", message, ()=>{}, null, true,{"Recommencer":()=>{
160 | dialog.close();
161 | restartMonopoly();
162 | }});
163 | },
164 | /* Calcule un score de victoire */
165 | /* Prend en compte l'argent, le nombre de terrains, le nombre de constructions, le nombre de tours des joueurs adverses */
166 | /* On pondere par rapport au nombre de joueur (plus il est grand, plus le nombre de maison a de l'importance) */
167 | _calculateScore(joueur){
168 | let statsJoueur = joueur.getStats();
169 | let critere1 = statsJoueur.argent/10000;
170 | let critere2 = (joueur.maisons.maisons.length*this.joueurs.length)/4; // < 1
171 | let critere3 = 1 + statsJoueur.hotel/12 + statsJoueur.maison/32; // < 2
172 | let critere4 = (statsJoueur.tour+1) * this.joueurs.length; // ~5 * nbJoueurs
173 | let critere5 = 1; // ~5 * nbJoueurs
174 | this.joueurs.forEach(j=>{
175 | critere5+=(!j.equals(joueur))?j.pion.stats.tour:0;
176 | });
177 | let score = (critere4 - critere5) * critere1 * critere2 * critere3;
178 | return Math.round(score);
179 | },
180 | _formatTempsJeu(beginTime){
181 | let time = Math.round((new Date().getTime() - beginTime)/1000);
182 | if(time < 60){
183 | return time + " sec";
184 | }
185 | const sec = time%60;
186 | time = Math.round(time/60);
187 | return `${time} min et ${sec} sec`;
188 | },
189 | _select(joueur){
190 | if(!joueur.canPlay){
191 | disableActions();
192 | }else{
193 | enableActions();
194 | }
195 | if(VARIANTES.echangeApresVente && GestionFiche.isFreeFiches()){
196 | document.getElementById('idEchangeTerrains').setAttribute('disabled','disabled');
197 | document.getElementById('idEchangeTerrains').classList.add('disabled');
198 | }
199 | if (!joueur.equals(this.joueurCourant)) {
200 | document.querySelectorAll('.joueurCourant').forEach(d=>d.classList.remove('joueurCourant'))
201 | if(this.joueurCourant!=null && this.joueurCourant.pion !=null){
202 | this.joueurCourant.pion.pion.setSelected(false);
203 | }
204 | }
205 |
206 | this.joueurCourant = joueur;
207 | this.joueurCourant.pion.pion.setSelected(true);
208 | joueur.select();
209 | },
210 | getWinner() {
211 | let defaites = 0;
212 | let gagnantProbable;
213 | for (let index in this.joueurs) {
214 | if (this.joueurs[index].defaite === true) {
215 | defaites++;
216 | } else {
217 | gagnantProbable = this.joueurs[index];
218 | }
219 | }
220 | if (defaites === this.joueurs.length - 1) {
221 | return gagnantProbable;
222 | }
223 | return null;
224 | },
225 | next(){
226 | if (this.joueurCourant == null) {
227 | return this.joueurs[0];
228 | }
229 | // On verifie s'il y a encore des joueurs "vivants"
230 | if (this.joueurCourant.bloque) {
231 | return null;
232 | }
233 | let gagnant = this.getWinner();
234 | if (gagnant != null) {
235 | // On a un vainqueur
236 | throw gagnant;
237 | }
238 | let joueur = this.joueurCourant;
239 | /* Changement de joueur */
240 | if(!GestionDes.continuePlayer() && !GestionDes.isSpecificAction()){
241 | let pos = 0;
242 | joueur = this.joueurs[(joueur.numero + 1) % (this.joueurs.length)];
243 | while (joueur.defaite === true && pos++ < this.joueurs.length) {
244 | joueur = this.joueurs[(joueur.numero + 1) % (this.joueurs.length)];
245 | }
246 | // On incremente le nb de tours
247 | if (joueur.numero < this.joueurCourant.numero) {
248 | globalStats.nbTours++;
249 | }
250 | }
251 | if(GestionDes.isSpecificAction()){
252 | GestionDes.doSpecificAction();
253 | // On ne laisse pas le joueur jouer, on enchaine l'action
254 | return null;
255 | }
256 | return joueur;
257 | },
258 | /* @param element : sera represente par "this" dans la methode callback */
259 | forEach(callback,element){
260 | this.joueurs.forEach(callback,element);
261 | }
262 | };
263 |
264 |
265 | window.gestion = GestionJoueur;
266 |
267 | // Construit les joueurs en fonction du context
268 | let JoueurFactory = {
269 | // Play with network
270 | network:false,
271 | // Current instance is master of network game
272 | master:true,
273 | setMouseFunction(fct){
274 | this.mouseFunction = fct;
275 | },
276 | useNetwork(){
277 | this.network = true;
278 | },
279 | useLocal(){
280 | this.network = false;
281 | },
282 | setMaster(){
283 | this.master = true;
284 | },
285 | setSlave(){
286 | this.master = false;
287 | },
288 | getRobotPlayer(){
289 | return this.network ? NetworkJoueurOrdinateur:JoueurOrdinateur;
290 | },
291 | getCurrentPlayer(){
292 | return this.network ? (this.master ? NetworkJoueur : LocalPlayer) : Joueur;
293 | },
294 | getOtherPlayer(){
295 | return this.network ? (this.master ? MasterRemotePlayer : RemotePlayer) : Joueur;
296 | }
297 | };
298 |
299 | export {GestionJoueur,JoueurFactory};
300 |
--------------------------------------------------------------------------------
/js/ui/circle_graphics.js:
--------------------------------------------------------------------------------
1 | import {DrawerFactory,Component} from "./graphics.js";
2 | import {Des,DesRapide} from './square_graphics.js'
3 | import {DrawerHelper} from "./graphics.js";
4 | import {VARIANTES} from "../core/monopoly.js";
5 | import {bus} from "../bus_message.js";
6 | import {GestionFiche} from "../display/case_jeu.js";
7 | import {deepCopy} from "../utils.js";
8 |
9 | /* Implementation pour plateau carree */
10 |
11 | let pasAngle = (2 * Math.PI)/40; // Nombre de case du jeu
12 | let nbJoueurs = 0; // Permet de definir la position des joueurs
13 |
14 | function getCoords(angle,rayon){
15 | return {
16 | y:(Math.sin(angle))*rayon,
17 | x:(Math.cos(angle))*rayon
18 | }
19 | }
20 |
21 | function convertAxePos(axe,pos){
22 | return ((axe+2)%4)*10 + pos;
23 | }
24 |
25 | // Represente un pion d'un joueur
26 | class CirclePionJoueur extends Component {
27 | constructor(color, largeur,img) {
28 | super();
29 | this.color = color;
30 | this.isSelected = false;
31 | this.largeur = largeur / 2; // Largeur du pion
32 | this.img = null;
33 | if (img != null) {
34 | this.img = new Image();
35 | this.img.src = img;
36 | this.largeur += 10;
37 | }
38 | this.init(2,0);
39 | }
40 |
41 | init(axe,pos){
42 | let center = DrawerFactory.dimensions.plateauSize/2;
43 | this.pos = convertAxePos(axe,pos);
44 | /* Gere le decalage de chaque joueur lors de la creation */
45 | this._rayon = center- (70 + (nbJoueurs%3)*25);
46 | this._angle = 0.5 + ((nbJoueurs%2)?1:-1) * 0.25;
47 | nbJoueurs++;
48 | }
49 |
50 | draw (canvas) {
51 | let centrePlateau = DrawerFactory.dimensions.plateauSize/2;
52 | let centre = getCoords((this.pos+this._angle)*pasAngle,this._rayon);
53 | if(this.isSelected){
54 | DrawerHelper.drawCircle(canvas,"#FFFFFF",(this.largeur+2) / 2,{x:centre.x+centrePlateau,y:centre.y+centrePlateau},"#FF0000");
55 | }
56 | if(this.img!=null){
57 | DrawerHelper.drawImage(canvas, this.img, centre.x+centrePlateau-this.largeur/2, centre.y+centrePlateau-this.largeur/2, this.largeur, this.largeur, 0);
58 | }
59 | else{
60 | DrawerHelper.drawCircle(canvas,this.color,this.largeur,{x:centre.x+centrePlateau,y:centre.y+centrePlateau},"#FF0000");
61 | }
62 | }
63 | setSelected (value){
64 | this.isSelected = value;
65 | }
66 |
67 | _moveTo (ciblePos,callback,pas){
68 | if(this.pos!==ciblePos){
69 | setTimeout(()=>{
70 | this.pos=parseFloat((this.pos + pas).toFixed(1));
71 | if(this.pos>=40){
72 | this.pos = 0;
73 | }
74 | this._moveTo(ciblePos,callback,pas);
75 | },30);
76 | }
77 | else{
78 | callback();
79 | }
80 | }
81 |
82 | // Se dirige vers une cellule donnee. Se deplace sur la case suivante et relance l'algo
83 | goto(axe, pos, callback) {
84 | if(VARIANTES.quickMove){
85 | return this.gotoDirect(axe,pos,callback);
86 | }
87 | let ciblePos = convertAxePos(axe,pos);
88 | this._moveTo(ciblePos,callback,0.1);
89 | }
90 |
91 | gotoDirect (axe, pos, callback){
92 | if (axe == null || pos == null) {
93 | return;
94 | }
95 | let ciblePos = convertAxePos(axe,pos);
96 | this._moveTo(ciblePos,callback,1);
97 | }
98 | }
99 |
100 | class CircleCase extends Component{
101 | constructor(pos, axe, color, title, prix, img) {
102 | super();
103 | this.pos = convertAxePos(axe, pos);
104 | this.color = color;
105 | this.title = title;
106 | this.prix = prix;
107 | this.img = img;
108 | this.nbMaison = 0;
109 | this.data = {};
110 | this.imageMaison = new Image();
111 | this.imageHotel = new Image();
112 | this.joueurPossede = null;
113 |
114 | this.init();
115 | }
116 | setJoueur(joueur){
117 | this.joueurPossede = joueur;
118 | }
119 |
120 | setNbMaison(nbMaison){
121 | this.nbMaison = nbMaison;
122 | }
123 |
124 | init(){
125 | this.imageMaison.src = "img/maison.png";
126 | this.imageHotel.src = "img/hotel.png";
127 |
128 | if (this.img != null) {
129 | this.img = deepCopy(DrawerFactory.infos.defaultImage, this.img);
130 | let image = new Image();
131 | image.addEventListener('load',()=>{
132 | bus.refresh();
133 | });
134 | image.src = this.img.src;
135 | image.height = Math.min(this.img.height,30);
136 | image.width = Math.min(this.img.width,40);
137 | image.margin = this.img.marginTop || 0;
138 | image.marginLeft = this.img.marginLeft || 0;
139 | image.rotate = this.img.rotate || 0;
140 | if(this.pos > 10 && this.pos < 30){
141 | image.rotate = (image.rotate+180)%360;
142 | image.marginLeft = image.marginLeft +0.5;
143 | image.margin=Math.max(0,image.margin-20);
144 | }
145 | this.data.image = image;
146 | }
147 | }
148 |
149 | _drawColorGroup(canvas,param){
150 | let bgColor = DrawerFactory.getInfo("backgroundColor");
151 | if(this.color!=null){
152 | canvas.beginPath();
153 | canvas.fillStyle=this.color;
154 | canvas.moveTo(param.centre,param.centre);
155 | canvas.arc(param.centre,param.centre,param.centre,(this.pos)*pasAngle,(this.pos+1)*pasAngle);
156 | canvas.fill();
157 | canvas.closePath();
158 | canvas.beginPath();
159 | canvas.fillStyle=bgColor;
160 | canvas.moveTo(param.centre,param.centre);
161 | canvas.arc(param.centre,param.centre,param.centre-param.bordure,this.pos*pasAngle,(this.pos+1)*pasAngle);
162 | canvas.fill();
163 | canvas.closePath();
164 | }
165 | }
166 |
167 | _drawPossede(canvas,param){
168 | if(this.joueurPossede!=null){
169 | canvas.beginPath();
170 | canvas.strokeStyle=this.joueurPossede.color;
171 | canvas.lineWidth = 10;
172 | canvas.arc(param.centre,param.centre,param.centre-DrawerFactory.dimensions.innerPlateauSize+15,(this.pos)*pasAngle,(this.pos+1)*pasAngle);
173 | canvas.stroke();
174 | canvas.closePath();
175 | }
176 | }
177 | _writeText(canvas,value,param,angle){
178 | DrawerHelper.writeText(value,param.p.x + param.centre,param.p.y + param.centre, angle*pasAngle, canvas,9,param.length,param.align);
179 | }
180 | _drawTitle(canvas,param){
181 | if(this.title!=null){
182 | if(this.pos > 10 && this.pos < 30){
183 | param.p = getCoords((this.pos+0.7)*pasAngle,param.centre-param.bordure -5);
184 | param.align='left';
185 | this._writeText(canvas,this.title,param,((this.pos+20)%40 + 0.8));
186 | }else{
187 | param.length = Math.min(param.length,canvas.measureText(this.title).width + 30);
188 | param.p = getCoords((this.pos+0.3)*pasAngle,param.centre -param.length - param.bordure -5);
189 | param.align='right';
190 | this._writeText(canvas,this.title,param,this.pos + 0.2);
191 | }
192 | }
193 | }
194 | _drawPrice(canvas,param){
195 | if(this.prix!=null){
196 | if(this.pos > 10 && this.pos < 30){
197 | param.p = getCoords((this.pos+0.15)*pasAngle,param.centre-param.bordure -5);
198 | param.align='left';
199 | this._writeText(canvas,this.prix,param,((this.pos+20)%40 + 0.5));
200 | }else{
201 | param.length = Math.min(param.length,canvas.measureText(this.prix).width + 30);
202 | param.p = getCoords((this.pos+0.9)*pasAngle,param.centre - param.length- param.bordure -5);
203 | param.align='right';
204 | this._writeText(canvas,this.prix,param,this.pos + 0.7);
205 | }
206 | }
207 | }
208 | _drawHouses(canvas,param){
209 | if(this.nbMaison <= 4){
210 | for(let i = 0 ; i < this.nbMaison ; i++){
211 | let coords = getCoords((this.pos + 0.25*i)*pasAngle,param.centre-4);
212 | DrawerHelper.drawImage(canvas, this.imageMaison, param.centre+coords.x, param.centre+coords.y, 16,16, this.pos*pasAngle + Math.PI/2)
213 | }
214 | }
215 | else{
216 | let coords = getCoords((this.pos + 0.4)*pasAngle,param.centre-4);
217 | DrawerHelper.drawImage(canvas, this.imageHotel, param.centre+coords.x, param.centre+coords.y, 16,16, this.pos*pasAngle + Math.PI/2)
218 | }
219 | }
220 | _drawImage(canvas,param){
221 | if (this.data.image != null) {
222 | // Margin left est defini en portion d'angle (1 correspond a la largeur de la case), margin top joue sur la longueur du rayon
223 | let coords = getCoords((this.pos + this.data.image.marginLeft)*pasAngle,param.centre-param.bordure -5 - this.data.image.margin);
224 | let angle = DrawerHelper.fromDegresToRad(this.data.image.rotate) + this.pos*pasAngle + Math.PI/2;
225 | DrawerHelper.drawImage(canvas, this.data.image, param.centre+coords.x, param.centre+coords.y, this.data.image.width,this.data.image.height, angle);
226 | }
227 | }
228 |
229 | draw(canvas){
230 | let centre = DrawerFactory.dimensions.plateauSize/2;
231 | let bordure = DrawerFactory.dimensions.bordure/2;
232 | let pA = getCoords(this.pos*pasAngle,centre);
233 | let pB = getCoords(this.pos*pasAngle,centre-DrawerFactory.dimensions.innerPlateauSize);
234 |
235 | canvas.fillStyle='#FFFFFF';
236 | canvas.lineWidth=0.5;
237 | canvas.moveTo(centre - pA.x,centre - pA.y);
238 | canvas.lineTo(centre - pB.x,centre - pB.y);
239 | canvas.stroke();
240 | let param = {centre:centre,bordure:bordure,length:140};
241 | this._drawColorGroup(canvas,param);
242 | this._drawTitle(canvas,param);
243 | this._drawPrice(canvas,param);
244 | this._drawImage(canvas,param);
245 | this._drawHouses(canvas,param);
246 | this._drawPossede(canvas,param);
247 | }
248 | }
249 |
250 | class CircleCaseSpeciale extends Component{
251 | constructor(axe, title){
252 | super();
253 | this.pos = convertAxePos(axe,0);
254 | this.title = title;
255 | }
256 | draw (canvas){
257 | let centre = DrawerFactory.dimensions.plateauSize/2;
258 | let pA = getCoords(this.pos*pasAngle,centre);
259 | let pB = getCoords(this.pos*pasAngle,centre-DrawerFactory.dimensions.innerPlateauSize);
260 | let bgColor = DrawerFactory.getInfo("backgroundColor");
261 | canvas.fillStyle=bgColor;
262 | canvas.lineWidth=0.5;
263 | canvas.moveTo(centre - pA.x,centre - pA.y);
264 | canvas.lineTo(centre - pB.x,centre - pB.y);
265 | canvas.stroke();
266 | DrawerHelper.drawArcCircle(canvas,bgColor,centre,{x:centre,y:centre},this.pos*pasAngle,(this.pos+1)*pasAngle);
267 | let maxLength = 120;
268 | if(this.title!=null){
269 | if(this.pos > 10 && this.pos < 30){
270 | let p = getCoords((this.pos+0.5)*pasAngle,centre-15);
271 | DrawerHelper.writeText(this.title, p.x + centre,p.y + centre, ((this.pos+20)%40 + 0.6)*pasAngle, canvas,9,maxLength,'left');
272 | }else{
273 | let p = getCoords((this.pos+0.6)*pasAngle,centre -maxLength - 15);
274 | DrawerHelper.writeText(this.title, p.x + centre,p.y + centre, (this.pos + 0.2)*pasAngle, canvas,9,maxLength,'right');
275 | }
276 | }
277 | }
278 | }
279 |
280 | /* Represente un dé physique */
281 | class CircleDes extends Des {
282 | constructor(x, y, width) {
283 | super(x + 195, y + 100, width);
284 | }
285 | }
286 |
287 | class CircleDesRapide extends DesRapide{
288 | constructor(x,y,width) {
289 | super(x + 190, y + 100, width);
290 | }
291 | }
292 |
293 | class CirclePlateau extends Component{
294 | constructor(x,y,width,height,color){
295 | super();
296 | this.data = {
297 | x: x,
298 | y: y,
299 | width: width,
300 | height: height,
301 | color:color
302 | };
303 | }
304 |
305 | draw(canvas) {
306 | DrawerHelper.drawCircle(canvas,this.data.color,this.data.width/2,{x:this.data.width/2,y:this.data.width/2});
307 | }
308 | enableCaseDetect(callback){
309 | const plateau = this;
310 | document.getElementById('canvas_rt').onmousedown = function(e){
311 | const x = e.clientX - this.parentNode.offsetLeft - this.width/2;
312 | const y = (e.clientY - this.parentNode.offsetTop - this.height/2) * -1;
313 | let angle = (Math.atan(y/x) / (2*Math.PI)) *360;
314 | if(x < 0){
315 | angle+=180;
316 | }else{
317 | if(y< 0){
318 | angle += 360;
319 | }
320 | }
321 | const fichePosition = (59 - Math.floor(angle / 9)) % 40;
322 | plateau.disableCaseDetect();
323 | callback(GestionFiche.fiches[fichePosition])
324 | }
325 | }
326 | disableCaseDetect(){
327 | document.getElementById('canvas_rt').onmousedown = null;
328 | }
329 | }
330 |
331 | /* Dessiné en dernier sur le plateau */
332 | class EndCirclePlateau extends Component{
333 | constructor(){
334 | super();
335 | }
336 |
337 | draw(canvas){
338 | let centre = DrawerFactory.dimensions.plateauSize/2;
339 | let innerPlateau = DrawerFactory.dimensions.innerPlateauSize;
340 | DrawerHelper.drawCircle(canvas,'#000000',centre-innerPlateau,{x:centre,y:centre});
341 | DrawerHelper.drawCircle(canvas,'#FFFFFF',centre-innerPlateau - 2,{x:centre,y:centre});
342 | DrawerHelper.drawArcCircle(canvas,'#FF0000',centre-innerPlateau -2,{x:centre,y:centre},-Math.PI,0);
343 | DrawerHelper.drawCircle(canvas,'#FFFFFF',centre-innerPlateau - 50,{x:centre,y:centre});
344 | }
345 | }
346 |
347 | function initCircleInstance(){
348 | let instance = {
349 | type:'circle',
350 | standardCase:CircleCase,
351 | specialCase:CircleCaseSpeciale,
352 | pionJoueur:CirclePionJoueur,
353 | des:CircleDes,
354 | desRapide:CircleDesRapide,
355 | plateau:CirclePlateau,
356 | endPlateau:EndCirclePlateau
357 | };
358 | DrawerFactory.addInstance(instance);
359 | }
360 |
361 | initCircleInstance();
--------------------------------------------------------------------------------
/lib/circletype.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * circletype 2.3.2
3 | * A JavaScript library that lets you curve type on the web.
4 | * Copyright © 2014-2020 Peter Hrynkow
5 | * Licensed MIT
6 | * https://github.com/peterhry/CircleType#readme
7 | */
8 | !function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.CircleType=n():t.CircleType=n()}(window,(function(){return function(t){var n={};function e(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}return e.m=t,e.c=n,e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:r})},e.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,n){if(1&n&&(t=e(t)),8&n)return t;if(4&n&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(e.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&n&&"string"!=typeof t)for(var i in t)e.d(r,i,function(n){return t[n]}.bind(null,i));return r},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},e.p="",e(e.s=28)}([function(t,n,e){var r=e(13)("wks"),i=e(12),o=e(1).Symbol,u="function"==typeof o;(t.exports=function(t){return r[t]||(r[t]=u&&o[t]||(u?o:i)("Symbol."+t))}).store=r},function(t,n){var e=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=e)},function(t,n){var e=t.exports={version:"2.6.11"};"number"==typeof __e&&(__e=e)},function(t,n,e){var r=e(4),i=e(11);t.exports=e(6)?function(t,n,e){return r.f(t,n,i(1,e))}:function(t,n,e){return t[n]=e,t}},function(t,n,e){var r=e(5),i=e(33),o=e(34),u=Object.defineProperty;n.f=e(6)?Object.defineProperty:function(t,n,e){if(r(t),n=o(n,!0),r(e),i)try{return u(t,n,e)}catch(t){}if("get"in e||"set"in e)throw TypeError("Accessors not supported!");return"value"in e&&(t[n]=e.value),t}},function(t,n,e){var r=e(10);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,n,e){t.exports=!e(18)((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},function(t,n){var e={}.hasOwnProperty;t.exports=function(t,n){return e.call(t,n)}},function(t,n){var e=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:e)(t)}},function(t,n){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},function(t,n){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},function(t,n){var e=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++e+r).toString(36))}},function(t,n,e){var r=e(2),i=e(1),o=i["__core-js_shared__"]||(i["__core-js_shared__"]={});(t.exports=function(t,n){return o[t]||(o[t]=void 0!==n?n:{})})("versions",[]).push({version:r.version,mode:e(16)?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(t,n){t.exports={}},function(t,n,e){var r=e(13)("keys"),i=e(12);t.exports=function(t){return r[t]||(r[t]=i(t))}},function(t,n){t.exports=!1},function(t,n,e){var r=e(1),i=e(2),o=e(3),u=e(20),c=e(21),a=function(t,n,e){var f,s,l,p,h=t&a.F,v=t&a.G,d=t&a.S,y=t&a.P,m=t&a.B,g=v?r:d?r[n]||(r[n]={}):(r[n]||{}).prototype,_=v?i:i[n]||(i[n]={}),x=_.prototype||(_.prototype={});for(f in v&&(e=n),e)l=((s=!h&&g&&void 0!==g[f])?g:e)[f],p=m&&s?c(l,r):y&&"function"==typeof l?c(Function.call,l):l,g&&u(g,f,l,t&a.U),_[f]!=l&&o(_,f,p),y&&x[f]!=l&&(x[f]=l)};r.core=i,a.F=1,a.G=2,a.S=4,a.P=8,a.B=16,a.W=32,a.U=64,a.R=128,t.exports=a},function(t,n){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,n,e){var r=e(10),i=e(1).document,o=r(i)&&r(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},function(t,n,e){var r=e(1),i=e(3),o=e(7),u=e(12)("src"),c=e(35),a=(""+c).split("toString");e(2).inspectSource=function(t){return c.call(t)},(t.exports=function(t,n,e,c){var f="function"==typeof e;f&&(o(e,"name")||i(e,"name",n)),t[n]!==e&&(f&&(o(e,u)||i(e,u,t[n]?""+t[n]:a.join(String(n)))),t===r?t[n]=e:c?t[n]?t[n]=e:i(t,n,e):(delete t[n],i(t,n,e)))})(Function.prototype,"toString",(function(){return"function"==typeof this&&this[u]||c.call(this)}))},function(t,n,e){var r=e(36);t.exports=function(t,n,e){if(r(t),void 0===n)return t;switch(e){case 1:return function(e){return t.call(n,e)};case 2:return function(e,r){return t.call(n,e,r)};case 3:return function(e,r,i){return t.call(n,e,r,i)}}return function(){return t.apply(n,arguments)}}},function(t,n,e){var r=e(42),i=e(9);t.exports=function(t){return r(i(t))}},function(t,n){var e={}.toString;t.exports=function(t){return e.call(t).slice(8,-1)}},function(t,n,e){var r=e(8),i=Math.min;t.exports=function(t){return t>0?i(r(t),9007199254740991):0}},function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,n,e){var r=e(4).f,i=e(7),o=e(0)("toStringTag");t.exports=function(t,n,e){t&&!i(t=e?t:t.prototype,o)&&r(t,o,{configurable:!0,value:n})}},function(t,n,e){var r=e(9);t.exports=function(t){return Object(r(t))}},function(t,n,e){e(29);var r=e(54).default;t.exports=r},function(t,n,e){e(30),e(47),t.exports=e(2).Array.from},function(t,n,e){"use strict";var r=e(31)(!0);e(32)(String,"String",(function(t){this._t=String(t),this._i=0}),(function(){var t,n=this._t,e=this._i;return e>=n.length?{value:void 0,done:!0}:(t=r(n,e),this._i+=t.length,{value:t,done:!1})}))},function(t,n,e){var r=e(8),i=e(9);t.exports=function(t){return function(n,e){var o,u,c=String(i(n)),a=r(e),f=c.length;return a<0||a>=f?t?"":void 0:(o=c.charCodeAt(a))<55296||o>56319||a+1===f||(u=c.charCodeAt(a+1))<56320||u>57343?t?c.charAt(a):o:t?c.slice(a,a+2):u-56320+(o-55296<<10)+65536}}},function(t,n,e){"use strict";var r=e(16),i=e(17),o=e(20),u=e(3),c=e(14),a=e(37),f=e(26),s=e(46),l=e(0)("iterator"),p=!([].keys&&"next"in[].keys()),h=function(){return this};t.exports=function(t,n,e,v,d,y,m){a(e,n,v);var g,_,x,b=function(t){if(!p&&t in S)return S[t];switch(t){case"keys":case"values":return function(){return new e(this,t)}}return function(){return new e(this,t)}},w=n+" Iterator",O="values"==d,j=!1,S=t.prototype,M=S[l]||S["@@iterator"]||d&&S[d],T=M||b(d),P=d?O?b("entries"):T:void 0,A="Array"==n&&S.entries||M;if(A&&(x=s(A.call(new t)))!==Object.prototype&&x.next&&(f(x,w,!0),r||"function"==typeof x[l]||u(x,l,h)),O&&M&&"values"!==M.name&&(j=!0,T=function(){return M.call(this)}),r&&!m||!p&&!j&&S[l]||u(S,l,T),c[n]=T,c[w]=h,d)if(g={values:O?T:b("values"),keys:y?T:b("keys"),entries:P},m)for(_ in g)_ in S||o(S,_,g[_]);else i(i.P+i.F*(p||j),n,g);return g}},function(t,n,e){t.exports=!e(6)&&!e(18)((function(){return 7!=Object.defineProperty(e(19)("div"),"a",{get:function(){return 7}}).a}))},function(t,n,e){var r=e(10);t.exports=function(t,n){if(!r(t))return t;var e,i;if(n&&"function"==typeof(e=t.toString)&&!r(i=e.call(t)))return i;if("function"==typeof(e=t.valueOf)&&!r(i=e.call(t)))return i;if(!n&&"function"==typeof(e=t.toString)&&!r(i=e.call(t)))return i;throw TypeError("Can't convert object to primitive value")}},function(t,n,e){t.exports=e(13)("native-function-to-string",Function.toString)},function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,n,e){"use strict";var r=e(38),i=e(11),o=e(26),u={};e(3)(u,e(0)("iterator"),(function(){return this})),t.exports=function(t,n,e){t.prototype=r(u,{next:i(1,e)}),o(t,n+" Iterator")}},function(t,n,e){var r=e(5),i=e(39),o=e(25),u=e(15)("IE_PROTO"),c=function(){},a=function(){var t,n=e(19)("iframe"),r=o.length;for(n.style.display="none",e(45).appendChild(n),n.src="javascript:",(t=n.contentWindow.document).open(),t.write("