diff options
author | Frans Bongers <fransbongers@franss-mbp.home> | 2024-12-15 16:02:52 +0100 |
---|---|---|
committer | Frans Bongers <fransbongers@franss-mbp.home> | 2024-12-15 16:02:52 +0100 |
commit | 47b19b5e83850b5fac6e508468b227476d486025 (patch) | |
tree | 6a9e5acd80060bfa410a62bbb514fa94343446a3 | |
parent | 7517f0e485f63c6c97ee2871ca64373375c504b8 (diff) | |
download | land-and-freedom-47b19b5e83850b5fac6e508468b227476d486025.tar.gz |
add tokens to fronts and log improvements
-rw-r--r-- | land-and-freedom.css | 85 | ||||
-rw-r--r-- | land-and-freedom.scss | 84 | ||||
-rw-r--r-- | play.html | 21 | ||||
-rw-r--r-- | play.js | 101 | ||||
-rw-r--r-- | play.ts | 125 | ||||
-rw-r--r-- | rules.js | 148 | ||||
-rw-r--r-- | rules.ts | 173 | ||||
-rw-r--r-- | types.d.ts | 22 |
8 files changed, 587 insertions, 172 deletions
diff --git a/land-and-freedom.css b/land-and-freedom.css index 196eddb..5fff93b 100644 --- a/land-and-freedom.css +++ b/land-and-freedom.css @@ -35,6 +35,7 @@ main { grid-row: 1; display: flex; flex-direction: column; + background: floralwhite; } #roles { @@ -43,22 +44,22 @@ main { } #role_Anarchist { - background-color: rgb(76, 70, 89); + background-color: rgb(93, 89, 106); } #role_Communist { - background-color: rgb(255, 0, 0); + background-color: rgb(237, 36, 27); } #role_Moderate { - background-color: rgb(126, 18, 80); + background-color: rgb(134, 44, 97); } .role { - color: white; + color: floralwhite; } .role a { - color: white; + color: floralwhite; } .panel { @@ -80,7 +81,7 @@ main { } .panel_header { - color: white; + color: floralwhite; user-select: none; font-weight: bold; text-align: center; @@ -93,15 +94,15 @@ main { } .panel_header[data-faction-id=a] { - background-color: rgb(76, 70, 89); + background-color: rgb(93, 89, 106); } .panel_header[data-faction-id=c] { - background-color: rgb(255, 0, 0); + background-color: rgb(237, 36, 27); } .panel_header[data-faction-id=m] { - background-color: rgb(126, 18, 80); + background-color: rgb(134, 44, 97); } .front { @@ -116,18 +117,30 @@ main { border-radius: 20px; } +.contributions { + display: flex; + flex-direction: row; + gap: 2px; + margin-top: 15px; + height: 25px; +} + .front .value { font-weight: bold; font-size: 30px; margin-top: auto; } -.front[data-front-id=m] { +#madrid_front { width: 104px; height: 114px; border-radius: 23px; } +#madrid_front .contributions { + padding: 0px 2px; +} + .card { background-size: 100% 100%; width: 198px; @@ -585,26 +598,35 @@ main { margin-top: 1px; } -.glory { +.faction_token { box-sizing: border-box; + background-size: 100% 100%; +} + +#glory .faction_token { position: absolute; width: 34px; height: 34px; - background-size: 100% 100%; border-radius: 4px; } -.glory[data-faction-id=a] { +.contributions .faction_token { + width: 25px; + height: 25px; + border-radius: 4px; +} + +.faction_token[data-faction-id=a] { border: 1px #333 solid; background-image: url("images/factions/anarchists.png"); } -.glory[data-faction-id=c] { +.faction_token[data-faction-id=c] { border: 1px #333 solid; background-image: url("images/factions/communitsts.png"); } -.glory[data-faction-id=m] { +.faction_token[data-faction-id=m] { border: 1px #333 solid; background-image: url("images/factions/moderates.png"); } @@ -745,9 +767,38 @@ main { } #log .h1 { - background-color: hsl(4, 40%, 73%); + background-color: rgb(233, 223, 200); } #log .h2 { - background-color: hsl(250, 40%, 83%); + background-color: rgb(233, 223, 200); +} + +#log .h2.anarchist { + background-color: rgb(93, 89, 106); + color: floralwhite; +} + +#log .h2.communist { + background-color: rgb(237, 36, 27); + color: floralwhite; +} + +#log .h2.moderate { + background-color: rgb(134, 44, 97); + color: floralwhite; +} + +#log .h2.fascist { + background-color: rgb(183, 144, 105); +} + +#log div.i { + padding-left: 32px; + text-indent: -12px; +} + +#log div.ii { + padding-left: 44px; + text-indent: -12px; } diff --git a/land-and-freedom.scss b/land-and-freedom.scss index 8b9636f..6385944 100644 --- a/land-and-freedom.scss +++ b/land-and-freedom.scss @@ -4,9 +4,11 @@ $selectable-color: white; // yellow; $selected-color: yellow; //blue; -$anarchist-color: rgb(76, 70, 89); -$communist-color: rgb(255, 0, 0); -$moderate-color: rgb(126, 18, 80); +$anarchist-color: rgb(93, 89, 106); +$communist-color: rgb(237, 36, 27); +$moderate-color: rgb(134, 44, 97); +$fascist-color: rgb(183, 144, 105); +$log-text-background-color: rgb(233 223 200); main { // background-color: rgb(213, 196, 131); @@ -57,6 +59,7 @@ main { grid-row: 1; display: flex; flex-direction: column; + background: floralwhite; } #roles { @@ -90,10 +93,10 @@ main { } .role { - color: white; + color:floralwhite; a { - color: white; + color:floralwhite; } } @@ -117,7 +120,7 @@ main { } .panel_header { - color: white; + color: floralwhite; user-select: none; font-weight: bold; text-align: center; @@ -165,18 +168,39 @@ main { // opacity: 0.5; } +.contributions { + display: flex; + flex-direction: row; + gap: 2px; + // width: 100%; + margin-top: 15px; + // background-color: yellow; + // opacity: 0.5; + height: 25px; +} + .front .value { font-weight: bold; font-size: 30px; margin-top: auto; } -.front[data-front-id='m'] { +#madrid_front { width: 104px; height: 114px; border-radius: 23px; } +#madrid_front .contributions { + padding: 0px 2px; +} + +// .front[data-front-id='m'] { +// width: 104px; +// height: 114px; +// border-radius: 23px; +// } + .card { background-size: 100% 100%; // width: 275px; @@ -212,29 +236,38 @@ main { margin-top: 1px; } -.glory { +.faction_token { box-sizing: border-box; - position: absolute; // background-color: yellow; // opacity: 0.5; - width: 34px; - height: 34px; background-size: 100% 100%; // box-shadow: 0 0 0 1px #333; +} + +#glory .faction_token { + position: absolute; + width: 34px; + height: 34px; border-radius: 4px; } -.glory[data-faction-id='a'] { +.contributions .faction_token { + width: 25px; + height: 25px; + border-radius: 4px; +} + +.faction_token[data-faction-id='a'] { border: 1px #333 solid; background-image: url('images/factions/anarchists.png'); } -.glory[data-faction-id='c'] { +.faction_token[data-faction-id='c'] { border: 1px #333 solid; background-image: url('images/factions/communitsts.png'); } -.glory[data-faction-id='m'] { +.faction_token[data-faction-id='m'] { border: 1px #333 solid; background-image: url('images/factions/moderates.png'); } @@ -327,8 +360,27 @@ main { text-align: center; } #log .h1 { - background-color: hsl(4, 40%, 73%); + // background-color: hsl(4, 40%, 73%); + background-color: $log-text-background-color; } #log .h2 { - background-color: hsl(250, 40%, 83%); + // background-color: hsl(250, 40%, 83%); + background-color: $log-text-background-color; +} +#log .h2.anarchist { + background-color: $anarchist-color; + color:floralwhite; +} +#log .h2.communist { + background-color: $communist-color; + color:floralwhite; +} +#log .h2.moderate { + background-color: $moderate-color; + color:floralwhite; +} +#log .h2.fascist { + background-color: $fascist-color; } +#log div.i { padding-left: 32px; text-indent: -12px; } +#log div.ii { padding-left: 44px; text-indent: -12px; }
\ No newline at end of file @@ -33,7 +33,7 @@ <aside> <div class="game_info"> - <span id="year">Year 1</span> + <span id="year"></span> <span id="pool_hero_points"></span> </div> <div id="roles"> @@ -62,6 +62,25 @@ <main> <div id="mapwrap"> <div id="map"> + <div id="fronts"> + <div id="northern_front" class="front" style="left: 89px; top: 96px;"> + <div class="contributions"></div> + <span class="value"></span> + </div> + <div id="aragon_front" class="front" style="left: 340px; top: 182px;"> + <div class="contributions"></div> + <span class="value"></span> + </div> + <div id="madrid_front" class="front" style="left: 115px; top: 262px;"> + <div class="contributions"></div> + <span class="value"></span> + </div> + <div id="southern_front" class="front" style="left: 205px; top: 426px;"> + <div class="contributions"></div> + <span class="value"></span> + </div> + </div> + <div id="glory"></div> <div id="spaces"> </div> <div id="pieces"></div> @@ -9,9 +9,32 @@ const TRACK_COUNT = 5; const TRACK_LENGTH = 11; const FACTIONS = ['a', 'c', 'm']; console.log('roles', document.getElementById('roles')); -let ui = { +const ui = { map: document.getElementById('map'), markers: document.getElementById('markers'), + fronts: { + a: { + front: document.getElementById('aragon_front'), + value: document.querySelector('#aragon_front .value'), + contributions: document.querySelector('#aragon_front .contributions'), + }, + m: { + front: document.getElementById('madrid_front'), + value: document.querySelector('#madrid_front .value'), + contributions: document.querySelector('#madrid_front .contributions'), + }, + n: { + front: document.getElementById('northern_front'), + value: document.querySelector('#northern_front .value'), + contributions: document.querySelector('#northern_front .contributions'), + }, + s: { + front: document.getElementById('southern_front'), + value: document.querySelector('#southern_front .value'), + contributions: document.querySelector('#southern_front .contributions'), + }, + }, + glory_container: document.getElementById('glory'), hand: document.getElementById('hand'), current_events: document.getElementById('current_events'), stats: { @@ -35,10 +58,10 @@ let ui = { }, tracks: document.getElementById('tracks'), hero_points: document.querySelector('#role_Anarchist .role_stat'), + year: document.getElementById('year'), blank_markers: [[], [], [], [], []], bonuses: [], - fronts: {}, - frontValues: {}, + tokens_on_front: {}, glory: [], medaillons: [], spaces: [], @@ -142,22 +165,6 @@ const LAYOUT_TRACKS = [ [948, 489], ], ]; -(function build_map() { - data.fronts.forEach((front, index) => { - const { id, top, left } = front; - const element = (ui.fronts[index] = document.createElement('div')); - element.classList.add('front'); - element.style.left = `${left}px`; - element.style.top = `${top}px`; - element.setAttribute('data-front-id', `${id}`); - ui.map.appendChild(element); - const frontValueElement = (ui.frontValues[front.id] = - document.createElement('span')); - frontValueElement.classList.add('value'); - register_action(element, 'front', id); - element.appendChild(frontValueElement); - }); -})(); console.log('ui', ui); function register_action(e, action, id) { e.my_action = action; @@ -205,8 +212,8 @@ function on_init() { } for (let g = 0; g < GLORY_COUNT; ++g) { let e = (ui.glory[g] = document.createElement('div')); - e.className = 'glory'; - ui.map.appendChild(ui.glory[g]); + e.className = 'faction_token'; + ui.glory_container.appendChild(ui.glory[g]); e.style.left = LAYOUT_GLORY[g][0] + 'px'; e.style.top = LAYOUT_GLORY[g][1] + 'px'; } @@ -224,14 +231,24 @@ function on_init() { register_action(e, 'standee', s); ui.tracks.appendChild(ui.standees[s]); } - console.log('standees', ui.standees); for (let c = 1; c < CARD_COUNT; ++c) { let e = (ui.cards[c] = document.createElement('div')); e.className = 'card'; e.setAttribute('data-card-id', '' + data.cards[c].id); register_action(e, 'card', c); } - console.log('action_register', action_register[0]); + data.fronts.forEach((front) => { + ui.tokens_on_front[front.id] = {}; + FACTIONS.forEach((faction_id) => { + let e = (ui.tokens_on_front[front.id][faction_id] = + document.createElement('div')); + e.className = 'faction_token'; + e.setAttribute('data-faction-id', faction_id); + }); + }); + Object.keys(ui.fronts).forEach((front_id) => { + register_action(ui.fronts[front_id].front, 'front', front_id); + }); } function on_update() { console.log('on_update', view); @@ -270,8 +287,13 @@ function on_update() { ui.standees[i].style.left = LAYOUT_TRACKS[i][view.tracks[i]][0] + 'px'; ui.standees[i].style.top = LAYOUT_TRACKS[i][view.tracks[i]][1] + 'px'; } - for (let frontId of Object.keys(view.fronts)) { - ui.frontValues[frontId].replaceChildren(view.fronts[frontId]); + for (let front_id of Object.keys(view.fronts)) { + const front_data = view.fronts[front_id]; + ui.fronts[front_id].value.replaceChildren(front_data.value); + ui.fronts[front_id].contributions.replaceChildren(); + for (let faction_id of front_data.contributions) { + ui.fronts[front_id].contributions.appendChild(ui.tokens_on_front[front_id][faction_id]); + } } for (let i = 0; i < view.medaillons.length; ++i) { if (view.medaillons[i] !== null) { @@ -291,6 +313,7 @@ function on_update() { } for (let e of action_register) e.classList.toggle('action', is_action(e.my_action, e.my_id)); + ui.year.replaceChildren(`Year ${view.year}`); action_button('add_to_front', '+1 to a Front'); action_button('soviet_support', 'Soviet Support'); action_button('collectivization', 'Collectivization'); @@ -322,6 +345,10 @@ function on_update() { } function on_log(text) { let p = document.createElement('div'); + if (text.match(/^>>/)) { + text = text.substring(2); + p.className = "ii"; + } if (text.match(/^>/)) { text = text.substring(1); p.className = 'i'; @@ -333,17 +360,25 @@ function on_log(text) { text = text.substring(4); p.className = 'h1'; } - else if (text.match(/^\.h2/)) { - text = text.substring(4); - p.className = 'h2'; + else if (text.match(/^\.h2\.Moderate/)) { + text = text.substring(13); + p.className = 'h2 moderate'; } - else if (text.match(/^\.h3\.allies/)) { - text = text.substring(10); - p.className = 'h3 allies'; + else if (text.match(/^\.h2\.Anarchist/)) { + text = text.substring(14); + p.className = 'h2 anarchist'; } - else if (text.match(/^\.h3\.germans/)) { + else if (text.match(/^\.h2\.Communist/)) { + text = text.substring(14); + p.className = 'h2 communist'; + } + else if (text.match(/^\.h2\.fascist/)) { text = text.substring(11); - p.className = 'h3 germans'; + p.className = 'h2 fascist'; + } + else if (text.match(/^\.h2/)) { + text = text.substring(4); + p.className = 'h2'; } else if (text.match(/^\.h3/)) { text = text.substring(4); @@ -1,6 +1,6 @@ 'use strict'; -import { FactionId, StaticData, View } from './types'; +import { StaticData, View } from './types'; declare function action_button(action: string, text: string): void; // declare function register_action(element: HTMLElement, type: string, s: number): void; @@ -23,9 +23,32 @@ const FACTIONS = ['a', 'c', 'm']; console.log('roles', document.getElementById('roles')); -let ui = { +const ui = { map: document.getElementById('map'), markers: document.getElementById('markers'), + fronts: { + a: { + front: document.getElementById('aragon_front'), + value: document.querySelector('#aragon_front .value'), + contributions: document.querySelector('#aragon_front .contributions'), + }, + m: { + front: document.getElementById('madrid_front'), + value: document.querySelector('#madrid_front .value'), + contributions: document.querySelector('#madrid_front .contributions'), + }, + n: { + front: document.getElementById('northern_front'), + value: document.querySelector('#northern_front .value'), + contributions: document.querySelector('#northern_front .contributions'), + }, + s: { + front: document.getElementById('southern_front'), + value: document.querySelector('#southern_front .value'), + contributions: document.querySelector('#southern_front .contributions'), + }, + }, + glory_container: document.getElementById('glory'), hand: document.getElementById('hand'), current_events: document.getElementById('current_events'), stats: { @@ -49,10 +72,12 @@ let ui = { }, tracks: document.getElementById('tracks'), hero_points: document.querySelector('#role_Anarchist .role_stat'), + year: document.getElementById('year'), blank_markers: [[], [], [], [], []], bonuses: [], - fronts: {}, - frontValues: {}, + // fronts: {}, + tokens_on_front: {}, + // front_values: {}, glory: [], medaillons: [], spaces: [], @@ -164,22 +189,26 @@ const LAYOUT_TRACKS = [ ]; // @ts-ignore -(function build_map() { - data.fronts.forEach((front, index) => { - const { id, top, left } = front; - const element = (ui.fronts[index] = document.createElement('div')); - element.classList.add('front'); - element.style.left = `${left}px`; - element.style.top = `${top}px`; - element.setAttribute('data-front-id', `${id}`); - ui.map.appendChild(element); - const frontValueElement = (ui.frontValues[front.id] = - document.createElement('span')); - frontValueElement.classList.add('value'); - register_action(element, 'front', id); - element.appendChild(frontValueElement); - }); -})(); +// (function build_map() { +// data.fronts.forEach((front) => { +// const { id, top, left } = front; +// const element = (ui.fronts[front.id] = document.createElement('div')); +// element.classList.add('front'); +// element.style.left = `${left}px`; +// element.style.top = `${top}px`; +// element.setAttribute('data-front-id', `${id}`); +// ui.map.appendChild(element); +// register_action(element, 'front', id); + +// // Contribution tokens + +// // Value of front +// const front_value_element = (ui.front_values[front.id] = +// document.createElement('span')); +// front_value_element.classList.add('value'); +// element.appendChild(front_value_element); +// }); +// })(); console.log('ui', ui); @@ -242,10 +271,11 @@ function on_init() { ui.map.appendChild(ui.bonuses[b]); } + // Create glory for (let g = 0; g < GLORY_COUNT; ++g) { let e = (ui.glory[g] = document.createElement('div')); - e.className = 'glory'; - ui.map.appendChild(ui.glory[g]); + e.className = 'faction_token'; + ui.glory_container.appendChild(ui.glory[g]); e.style.left = LAYOUT_GLORY[g][0] + 'px'; e.style.top = LAYOUT_GLORY[g][1] + 'px'; } @@ -268,8 +298,6 @@ function on_init() { ui.tracks.appendChild(ui.standees[s]); } - console.log('standees', ui.standees); - // create card elements for (let c = 1; c < CARD_COUNT; ++c) { let e = (ui.cards[c] = document.createElement('div')); @@ -278,7 +306,20 @@ function on_init() { register_action(e, 'card', c); } - console.log('action_register', action_register[0]); + // create tokens to add to fronts + data.fronts.forEach((front) => { + ui.tokens_on_front[front.id] = {}; + FACTIONS.forEach((faction_id) => { + let e = (ui.tokens_on_front[front.id][faction_id] = + document.createElement('div')); + e.className = 'faction_token'; + e.setAttribute('data-faction-id', faction_id); + }); + }); + + Object.keys(ui.fronts).forEach((front_id) => { + register_action(ui.fronts[front_id].front, 'front', front_id); + }) } // @ts-ignore @@ -338,8 +379,13 @@ function on_update() { ui.standees[i].style.top = LAYOUT_TRACKS[i][view.tracks[i]][1] + 'px'; } - for (let frontId of Object.keys(view.fronts)) { - ui.frontValues[frontId].replaceChildren(view.fronts[frontId]); + for (let front_id of Object.keys(view.fronts)) { + const front_data = view.fronts[front_id]; + ui.fronts[front_id].value.replaceChildren(front_data.value); + ui.fronts[front_id].contributions.replaceChildren(); + for(let faction_id of front_data.contributions) { + ui.fronts[front_id].contributions.appendChild(ui.tokens_on_front[front_id][faction_id]); + } } for (let i = 0; i < view.medaillons.length; ++i) { @@ -364,6 +410,8 @@ function on_update() { for (let e of action_register) e.classList.toggle('action', is_action(e.my_action, e.my_id)); + ui.year.replaceChildren(`Year ${view.year}`); + action_button('add_to_front', '+1 to a Front'); action_button('soviet_support', 'Soviet Support'); action_button('collectivization', 'Collectivization'); @@ -401,6 +449,11 @@ function on_update() { function on_log(text) { let p = document.createElement('div'); + if (text.match(/^>>/)) { + text = text.substring(2) + p.className = "ii" + } + if (text.match(/^>/)) { text = text.substring(1); p.className = 'i'; @@ -419,15 +472,21 @@ function on_log(text) { if (text.match(/^\.h1/)) { text = text.substring(4); p.className = 'h1'; + } else if (text.match(/^\.h2\.Moderate/)) { + text = text.substring(13); + p.className = 'h2 moderate'; + } else if (text.match(/^\.h2\.Anarchist/)) { + text = text.substring(14); + p.className = 'h2 anarchist'; + } else if (text.match(/^\.h2\.Communist/)) { + text = text.substring(14); + p.className = 'h2 communist'; + } else if (text.match(/^\.h2\.fascist/)) { + text = text.substring(11); + p.className = 'h2 fascist'; } else if (text.match(/^\.h2/)) { text = text.substring(4); p.className = 'h2'; - } else if (text.match(/^\.h3\.allies/)) { - text = text.substring(10); - p.className = 'h3 allies'; - } else if (text.match(/^\.h3\.germans/)) { - text = text.substring(11); - p.className = 'h3 germans'; } else if (text.match(/^\.h3/)) { text = text.substring(4); p.className = 'h3'; @@ -113,18 +113,27 @@ function setup_bag_of_glory() { next(); } function setup_choose_card() { - console.log('setup_choose_card'); const player_order = get_player_order(); game.engine = player_order.map((faction_id) => create_leaf_node('choose_card', faction_id)); game.engine.push(create_function_node('setup_player_turn')); - resolve_active_and_proceed(); + next(); } function setup_player_turn() { - console.log('setup_player_turn'); const player_order = get_player_order(); - game.engine = player_order.map((faction_id) => create_seq_node([create_leaf_node('player_turn', faction_id)])); + game.engine = player_order.map((faction_id) => create_seq_node([ + create_function_node('start_of_player_turn', { f: faction_id }), + create_leaf_node('player_turn', faction_id), + ])); game.engine.push(create_function_node('resolve_fascist_test')); game.engine.push(create_function_node('setup_bag_of_glory')); + next(); +} +function start_of_player_turn() { + const args = get_active_node_args(); + console.log('args', args); + console.log('args'); + const player = faction_player_map[args.f]; + log_h2(player, player); resolve_active_and_proceed(); } const engine_functions = { @@ -134,6 +143,7 @@ const engine_functions = { setup_bag_of_glory, setup_choose_card, setup_player_turn, + start_of_player_turn, resolve_fascist_test, }; function get_active(engine) { @@ -156,7 +166,7 @@ function get_active_node(engine = game.engine) { } function get_active_node_args() { const node = get_active_node(game.engine); - if (node.t === leaf_node) { + if (node.t === leaf_node || node.t === function_node) { return node.a; } return null; @@ -223,11 +233,13 @@ function game_view(state, player) { glory: game.glory, hand: game.hands[faction_id], hero_points: game.hero_points, + initiative: game.initiative, medaillons: game.medaillons, selected_card: game.chosen_cards[faction_id], tableaus: game.tableaus, tracks: game.tracks, triggered_track_effects: game.triggered_track_effects, + year: game.year, }; if (player !== game.active) { let inactive = states[game.state].inactive || game.state; @@ -260,10 +272,22 @@ function setup(seed, _scenario, _options) { }, engine: [], fronts: { - a: -2, - m: -2, - n: -2, - s: -2, + a: { + value: -2, + contributions: [], + }, + m: { + value: -2, + contributions: [], + }, + n: { + value: -2, + contributions: [], + }, + s: { + value: -2, + contributions: [], + }, }, glory: [], hands: { @@ -313,27 +337,24 @@ function setup(seed, _scenario, _options) { } function draw_hand_cards() { role_ids.forEach((role) => { - const deck = faction_cards[role]; + const deck = list_deck(role); for (let i = 0; i < 5; i++) { - game.hands[role]; game.hands[role].push(draw_card(deck)); } }); } function start_year() { - console.log('start year'); - log_h1('Year ' + game.year); game.current_events = []; draw_hand_cards(); start_turn(); } function start_turn() { - log_h2('Turn ' + game.turn); - const deck = fascist_decks[game.year]; - const cardId = draw_card(deck); + log_h1(`Year ${game.year} - Turn ${game.turn}`); + const cardId = draw_card(list_deck('fascist')); game.current_events.push(cardId); const card = cards[cardId]; - log_h3('Fascist Event: ' + card.title); + log_h2('Fascist Event', 'fascist'); + log(card.title); game.engine = card.effects.map((effect) => resolve_effect(effect, game.initiative)); game.engine.push({ t: 'f', @@ -475,10 +496,19 @@ states.choose_area_ap = { for (const front of fronts) { gen_action_front(front); } + for (const bonus of bonuses) { + if (game.bonuses[bonus] === data_1.OFF) { + gen_action_bonus(bonus); + } + } + }, + bonus(b) { + update_bonus(b, data_1.ON); + resolve_active_and_proceed(); }, front(f) { const s = get_active_node_args().strength; - update_front(f, s); + update_front(f, s, get_active_faction_id()); resolve_active_and_proceed(); }, standee(track_id) { @@ -520,8 +550,7 @@ states.change_bonus = { }, bonus(b) { const value = get_active_node_args().v; - game.bonuses[b] = value; - log(`${bonus_names[b]} ${value === data_1.ON ? 'on' : 'off'}`); + update_bonus(b, value); resolve_active_and_proceed(); }, skip() { @@ -712,8 +741,27 @@ function check_activate_icon() { } resolve_active_and_proceed(); } +function check_initiative() { + let initiative; + if (game.tracks[data_1.LIBERTY] >= 6 && game.tracks[data_1.COLLECTIVIZATION] >= 6) { + initiative = data_1.ANARCHISTS_ID; + } + else if (game.tracks[data_1.GOVERNMENT] <= 5) { + initiative = data_1.COMMUNISTS_ID; + } + else { + initiative = data_1.MODERATES_ID; + } + if (game.initiative === initiative) { + return; + } + game.initiative = initiative; + logi(`${faction_player_map[initiative]} claims the Initiative`); +} function end_of_turn() { - log_h2('End of turn'); + Object.keys(game.fronts).forEach((front_id) => { + game.fronts[front_id].contributions = []; + }); if (game.turn === 4) { end_of_year(); } @@ -734,9 +782,9 @@ function end_of_year() { } function resolve_fascist_test() { console.log('resolve fascist test'); - log_h2('Fascist test is resolved'); + log_h2('Fascist Test', 'fascist'); const test = get_current_event().test; - const test_passed = game.fronts[test.front] >= test.value; + const test_passed = game.fronts[test.front].value >= test.value; if (test_passed) { log('The Test is passed'); } @@ -768,7 +816,8 @@ function move_track(track_id, change) { new_value = Math.max(new_value, track_id === data_1.GOVERNMENT ? 1 : 0); new_value = Math.min(new_value, 10); game.tracks[track_id] = new_value; - log(`${get_track_name(track_id)} to ${new_value}`); + logi(`${get_track_name(track_id)} to ${new_value}`); + check_initiative(); const triggered_spaces = change > 0 ? make_list(current_value + 1, new_value).reverse() : make_list(new_value, current_value - 1); @@ -786,9 +835,28 @@ function move_track(track_id, change) { } }); } -function update_front(f, change) { - game.fronts[f] += change; - log(`${front_names[f]}: ${change > 0 ? '+' : ''}${change}`); +function update_bonus(bonus_id, status) { + if (game.bonuses[bonus_id] === status) { + return; + } + game.bonuses[bonus_id] = status; + logi(`${bonus_names[bonus_id]} ${status === data_1.ON ? 'on' : 'off'}`); +} +function update_front(f, change, faction_id = null) { + const player_token_on_front = faction_id !== null && game.fronts[f].contributions.includes(faction_id); + if (game.bonuses[data_1.TEAMWORK_BONUS] === data_1.ON && + change > 0 && + faction_id !== null && + !player_token_on_front && + game.fronts[f].contributions.length > 0) { + change += 1; + } + game.fronts[f].value += change; + logi(`${front_names[f]}: ${change > 0 ? '+' : ''}${change}`); + if (faction_id !== null && + !game.fronts[f].contributions.includes(faction_id)) { + game.fronts[f].contributions.push(faction_id); + } } function create_effects_node(effects) { const nodes = effects.reduce((accrued, current) => { @@ -855,9 +923,9 @@ function lose_hero_point(faction, value) { } } function get_fronts_closest_to(target) { - const values = Object.values(game.fronts); + const values = Object.values(game.fronts).map((front) => front.value); const targetValue = target === 'd' ? Math.min(...values) : Math.max(...values); - return Object.keys(game.fronts).filter((frontId) => game.fronts[frontId] === targetValue); + return Object.keys(game.fronts).filter((frontId) => game.fronts[frontId].value === targetValue); } function log_br() { if (game.log.length > 0 && game.log[game.log.length - 1] !== '') @@ -866,16 +934,20 @@ function log_br() { function log(msg) { game.log.push(msg); } +function logi(msg) { + log('>' + msg); +} function log_h1(msg) { log_br(); log('.h1 ' + msg); log_br(); } -function log_h2(msg) { +function log_h2(msg, player) { log_br(); - log('.h2 ' + msg); + log(`.h2${player ? `.${player}` : ''} ${msg}`); log_br(); } +log; function log_h3(msg) { log_br(); log('.h3 ' + msg); @@ -983,6 +1055,20 @@ function set_delete(set, item) { } } } +function list_deck(id) { + const deck = []; + const card_list = id === 'fascist' ? fascist_decks[game.year] : faction_cards[id]; + card_list.forEach((card) => { + if (id === 'fascist' && (game.discard.f.includes(card))) { + return; + } + else if (id !== 'fascist' && (game.hands[id].includes(card) || game.discard[id].includes(card))) { + return; + } + deck.push(card); + }); + return deck; +} function array_remove(array, index) { let n = array.length; for (let i = index + 1; i < n; ++i) @@ -214,23 +214,33 @@ function setup_bag_of_glory() { } function setup_choose_card() { - console.log('setup_choose_card'); const player_order = get_player_order(); game.engine = player_order.map((faction_id) => create_leaf_node('choose_card', faction_id) ); game.engine.push(create_function_node('setup_player_turn')); - resolve_active_and_proceed(); + next(); } function setup_player_turn() { - console.log('setup_player_turn'); const player_order = get_player_order(); game.engine = player_order.map((faction_id) => - create_seq_node([create_leaf_node('player_turn', faction_id)]) + create_seq_node([ + create_function_node('start_of_player_turn', { f: faction_id }), + create_leaf_node('player_turn', faction_id), + ]) ); game.engine.push(create_function_node('resolve_fascist_test')); game.engine.push(create_function_node('setup_bag_of_glory')); + next(); +} + +function start_of_player_turn() { + const args = get_active_node_args(); + console.log('args', args); + console.log('args'); + const player = faction_player_map[args.f]; + log_h2(player, player); resolve_active_and_proceed(); } @@ -241,6 +251,7 @@ const engine_functions: Record<string, Function> = { setup_bag_of_glory, setup_choose_card, setup_player_turn, + start_of_player_turn, resolve_fascist_test, }; @@ -300,7 +311,7 @@ function get_active_node( function get_active_node_args(): any { const node = get_active_node(game.engine); - if (node.t === leaf_node) { + if (node.t === leaf_node || node.t === function_node) { return node.a; } return null; @@ -390,11 +401,13 @@ function game_view(state: Game, player: Player) { glory: game.glory, hand: game.hands[faction_id], hero_points: game.hero_points, + initiative: game.initiative, medaillons: game.medaillons, selected_card: game.chosen_cards[faction_id], tableaus: game.tableaus, tracks: game.tracks, triggered_track_effects: game.triggered_track_effects, + year: game.year, }; if (player !== game.active) { @@ -432,10 +445,22 @@ export function setup(seed: number, _scenario: string, _options: unknown) { }, engine: [], fronts: { - a: -2, - m: -2, - n: -2, - s: -2, + a: { + value: -2, + contributions: [], + }, + m: { + value: -2, + contributions: [], + }, + n: { + value: -2, + contributions: [], + }, + s: { + value: -2, + contributions: [], + }, }, glory: [], hands: { @@ -487,9 +512,8 @@ export function setup(seed: number, _scenario: string, _options: unknown) { function draw_hand_cards() { role_ids.forEach((role) => { - const deck = faction_cards[role]; + const deck = list_deck(role); for (let i = 0; i < 5; i++) { - game.hands[role]; game.hands[role].push(draw_card(deck)); } }); @@ -498,22 +522,21 @@ function draw_hand_cards() { // #endregion function start_year() { - console.log('start year') - log_h1('Year ' + game.year); + // log_h1('Year ' + game.year); game.current_events = []; draw_hand_cards(); start_turn(); } function start_turn() { - log_h2('Turn ' + game.turn); + log_h1(`Year ${game.year} - Turn ${game.turn}`); - const deck = fascist_decks[game.year]; - const cardId = draw_card(deck); + const cardId = draw_card(list_deck('fascist')); game.current_events.push(cardId); const card = cards[cardId] as EventCard; - log_h3('Fascist Event: ' + card.title); + log_h2('Fascist Event', 'fascist'); + log(card.title); game.engine = card.effects.map((effect) => resolve_effect(effect, game.initiative) @@ -676,10 +699,20 @@ states.choose_area_ap = { for (const front of fronts) { gen_action_front(front); } + for (const bonus of bonuses) { + if (game.bonuses[bonus] === OFF) { + gen_action_bonus(bonus); + } + } + }, + bonus(b: number) { + update_bonus(b, ON); + // TODO: insert action in case other bonus is OFF and AP left + resolve_active_and_proceed(); }, front(f: string) { - const s = get_active_node_args().strength; - update_front(f, s); + const s: number = get_active_node_args().strength; + update_front(f, s, get_active_faction_id()); resolve_active_and_proceed(); }, standee(track_id: number) { @@ -723,8 +756,7 @@ states.change_bonus = { }, bonus(b: number) { const value = get_active_node_args().v; - game.bonuses[b] = value; - log(`${bonus_names[b]} ${value === ON ? 'on' : 'off'}`); + update_bonus(b, value); resolve_active_and_proceed(); }, skip() { @@ -1009,9 +1041,28 @@ function check_activate_icon() { resolve_active_and_proceed(); } +function check_initiative() { + let initiative: FactionId; + if (game.tracks[LIBERTY] >= 6 && game.tracks[COLLECTIVIZATION] >= 6) { + initiative = ANARCHISTS_ID; + } else if (game.tracks[GOVERNMENT] <= 5) { + initiative = COMMUNISTS_ID; + } else { + initiative = MODERATES_ID; + } + + if (game.initiative === initiative) { + return; + } + game.initiative = initiative; + logi(`${faction_player_map[initiative]} claims the Initiative`); +} + function end_of_turn() { - // REMOVE player tokens from the Fronts; - log_h2('End of turn'); + Object.keys(game.fronts).forEach((front_id) => { + game.fronts[front_id].contributions = []; + }); + // log_h2('End of turn'); if (game.turn === 4) { end_of_year(); } else { @@ -1034,10 +1085,10 @@ function end_of_year() { function resolve_fascist_test() { console.log('resolve fascist test'); - log_h2('Fascist test is resolved'); + log_h2('Fascist Test', 'fascist'); const test = get_current_event().test; - const test_passed = game.fronts[test.front] >= test.value; + const test_passed = game.fronts[test.front].value >= test.value; if (test_passed) { log('The Test is passed'); } else { @@ -1075,7 +1126,10 @@ function move_track(track_id: number, change: number) { new_value = Math.max(new_value, track_id === GOVERNMENT ? 1 : 0); new_value = Math.min(new_value, 10); game.tracks[track_id] = new_value; - log(`${get_track_name(track_id)} to ${new_value}`); + logi(`${get_track_name(track_id)} to ${new_value}`); + + check_initiative(); + const triggered_spaces = change > 0 ? make_list(current_value + 1, new_value).reverse() @@ -1098,10 +1152,39 @@ function move_track(track_id: number, change: number) { }); } +function update_bonus(bonus_id: number, status: number) { + if (game.bonuses[bonus_id] === status) { + return; + } + game.bonuses[bonus_id] = status; + logi(`${bonus_names[bonus_id]} ${status === ON ? 'on' : 'off'}`); +} + // TODO: acccount for victory / defeat of front -function update_front(f: string, change: number) { - game.fronts[f] += change; - log(`${front_names[f]}: ${change > 0 ? '+' : ''}${change}`); +function update_front( + f: string, + change: number, + faction_id: FactionId | null = null +) { + const player_token_on_front = + faction_id !== null && game.fronts[f].contributions.includes(faction_id); + if ( + game.bonuses[TEAMWORK_BONUS] === ON && + change > 0 && + faction_id !== null && + !player_token_on_front && + game.fronts[f].contributions.length > 0 + ) { + change += 1; + } + game.fronts[f].value += change; + logi(`${front_names[f]}: ${change > 0 ? '+' : ''}${change}`); + if ( + faction_id !== null && + !game.fronts[f].contributions.includes(faction_id) + ) { + game.fronts[f].contributions.push(faction_id); + } } function create_effects_node(effects: Effect[]): EngineNode { @@ -1276,11 +1359,11 @@ function lose_hero_point(faction: FactionId, value: number) { // #region FRONTS function get_fronts_closest_to(target: 'd' | 'v') { - const values = Object.values(game.fronts); + const values = Object.values(game.fronts).map((front) => front.value); const targetValue = target === 'd' ? Math.min(...values) : Math.max(...values); return Object.keys(game.fronts).filter( - (frontId) => game.fronts[frontId] === targetValue + (frontId) => game.fronts[frontId].value === targetValue ); } @@ -1305,12 +1388,12 @@ function log(msg: string) { // game.log.push(`C${cap}.`) // } -// function logi(msg: string) { -// game.log.push('>' + msg); -// } +function logi(msg: string) { + log('>' + msg); +} // function logii(msg: string) { -// game.log.push('>>' + msg); +// log('>>' + msg); // } function log_h1(msg: string) { @@ -1319,12 +1402,14 @@ function log_h1(msg: string) { log_br(); } -function log_h2(msg: string) { +function log_h2(msg: string, player?: Player | 'fascist') { log_br(); - log('.h2 ' + msg); + log(`.h2${player ? `.${player}` : ''} ${msg}`); log_br(); } +log; + // function log_h2_active(msg: string) { // log_br(); // log('.h2 ' + msg); @@ -1473,6 +1558,20 @@ function set_delete<T>(set: T[], item: T) { } } +function list_deck(id: FactionId | 'fascist') { + const deck = []; + const card_list = id === 'fascist' ? fascist_decks[game.year] : faction_cards[id]; + card_list.forEach((card) => { + if (id === 'fascist' && (game.discard.f.includes(card))) { + return + } else if (id !== 'fascist' && (game.hands[id].includes(card) || game.discard[id].includes(card))) { + return; + } + deck.push(card) + }) + return deck; +} + // #endregion // #region ARRAY @@ -27,10 +27,22 @@ export interface Game { discard: Record<FactionId | 'f', number[]>; engine: EngineNode[]; fronts: { - a: number; - m: number; - n: number; - s: number; + a: { + value: number; + contributions: FactionId[]; + }; + m: { + value: number; + contributions: FactionId[]; + }; + n: { + value: number; + contributions: FactionId[]; + }; + s: { + value: number; + contributions: FactionId[]; + }; }; glory: FactionId[]; hands: Record<FactionId, CardId[]>; @@ -71,10 +83,12 @@ export interface View { glory: Game['glory']; hand: CardId[]; hero_points: Game['hero_points']; + initiative: Game['initiative']; medaillons: Game['medaillons']; tableaus: Game['tableaus']; tracks: number[]; triggered_track_effects: Game['triggered_track_effects']; + year: number; } export type States = { |