From 5c3bd9b3802e2001a7e77baf2911386135a03839 Mon Sep 17 00:00:00 2001 From: notoraptor Date: Wed, 14 Aug 2019 12:22:22 -0400 Subject: [Web] Integrated new maps on the web interface - Fixed bug with incorrect dislodged unit on pure map - [python] Make sure dummy powers are registered only for standard maps. - Hardcoded supply centers into SVG files. - Removed supply centers CSS classes. - Update positions for units and dislodged units on all maps. - Converted SVGs to React. - Removed "sym" classes and hardcode related styles into symbol definitions. - Reordered map list (standard at top, then other ones in alphabetical order) - Displayed + button for all maps and disable it for maps without variants. - Minified generated code when converting SVG files to React. - [web] Added ability to hide/display map abbreviations. --- diplomacy/engine/renderer.py | 399 ++- diplomacy/integration/webdiplomacy_net/api.py | 2 +- diplomacy/maps/svg/ancmed.svg | 1593 ++++++------ diplomacy/maps/svg/modern.svg | 2579 ++++++++++---------- diplomacy/maps/svg/pure.svg | 338 ++- diplomacy/maps/svg/standard.svg | 599 +++-- diplomacy/server/server.py | 3 + diplomacy/utils/equilateral_triangle.py | 173 ++ diplomacy/web/convert_svg_maps_to_react.sh | 5 + diplomacy/web/src/gui/components/fancyBox.js | 4 +- diplomacy/web/src/gui/forms/join_form.jsx | 4 +- diplomacy/web/src/gui/maps/ancmed/SvgAncMed.css | 19 + diplomacy/web/src/gui/maps/ancmed/SvgAncMed.js | 20 + .../web/src/gui/maps/ancmed/SvgAncMedMetadata.js | 21 + diplomacy/web/src/gui/maps/common/build.js | 57 + diplomacy/web/src/gui/maps/common/common.js | 83 + diplomacy/web/src/gui/maps/common/convoy.js | 103 + diplomacy/web/src/gui/maps/common/disband.js | 47 + .../web/src/gui/maps/common/equilateralTriangle.js | 122 + diplomacy/web/src/gui/maps/common/hold.js | 47 + diplomacy/web/src/gui/maps/common/move.js | 67 + diplomacy/web/src/gui/maps/common/supportHold.js | 72 + diplomacy/web/src/gui/maps/common/supportMove.js | 61 + diplomacy/web/src/gui/maps/common/unit.js | 50 + diplomacy/web/src/gui/maps/modern/SvgModern.css | 19 + diplomacy/web/src/gui/maps/modern/SvgModern.js | 20 + .../web/src/gui/maps/modern/SvgModernMetadata.js | 21 + diplomacy/web/src/gui/maps/pure/SvgPure.css | 19 + diplomacy/web/src/gui/maps/pure/SvgPure.js | 20 + diplomacy/web/src/gui/maps/pure/SvgPureMetadata.js | 21 + .../web/src/gui/maps/standard/SvgStandard.css | 125 +- diplomacy/web/src/gui/maps/standard/SvgStandard.js | 761 +----- .../web/src/gui/maps/standard/SvgStandardExtra.js | 1279 ---------- .../src/gui/maps/standard/SvgStandardMetadata.js | 21 + diplomacy/web/src/gui/maps/standard/build.js | 53 - diplomacy/web/src/gui/maps/standard/common.js | 75 - diplomacy/web/src/gui/maps/standard/convoy.js | 98 - diplomacy/web/src/gui/maps/standard/disband.js | 51 - diplomacy/web/src/gui/maps/standard/hold.js | 45 - diplomacy/web/src/gui/maps/standard/move.js | 69 - .../web/src/gui/maps/standard/supplyCenter.js | 40 - diplomacy/web/src/gui/maps/standard/supportHold.js | 73 - diplomacy/web/src/gui/maps/standard/supportMove.js | 57 - diplomacy/web/src/gui/maps/standard/unit.js | 45 - diplomacy/web/src/gui/pages/content_connection.jsx | 2 + diplomacy/web/src/gui/pages/content_game.jsx | 140 +- diplomacy/web/src/gui/pages/content_games.jsx | 2 +- diplomacy/web/src/gui/utils/inline_game_view.jsx | 9 +- diplomacy/web/src/gui/utils/map_data.js | 24 +- .../wizards/gameCreation/gameCreationWizard.css | 31 + .../gui/wizards/gameCreation/gameCreationWizard.js | 1 + .../web/src/gui/wizards/gameCreation/mapList.js | 6 + .../src/gui/wizards/gameCreation/panelChooseMap.js | 9 +- .../gui/wizards/gameCreation/panelChoosePlayers.js | 50 +- .../gui/wizards/gameCreation/panelChoosePower.js | 50 +- diplomacy/web/src/index.css | 28 +- diplomacy/web/svg_to_react.py | 640 ++++- 57 files changed, 4610 insertions(+), 5762 deletions(-) create mode 100644 diplomacy/utils/equilateral_triangle.py create mode 100755 diplomacy/web/convert_svg_maps_to_react.sh create mode 100644 diplomacy/web/src/gui/maps/ancmed/SvgAncMed.css create mode 100644 diplomacy/web/src/gui/maps/ancmed/SvgAncMed.js create mode 100644 diplomacy/web/src/gui/maps/ancmed/SvgAncMedMetadata.js create mode 100644 diplomacy/web/src/gui/maps/common/build.js create mode 100644 diplomacy/web/src/gui/maps/common/common.js create mode 100644 diplomacy/web/src/gui/maps/common/convoy.js create mode 100644 diplomacy/web/src/gui/maps/common/disband.js create mode 100644 diplomacy/web/src/gui/maps/common/equilateralTriangle.js create mode 100644 diplomacy/web/src/gui/maps/common/hold.js create mode 100644 diplomacy/web/src/gui/maps/common/move.js create mode 100644 diplomacy/web/src/gui/maps/common/supportHold.js create mode 100644 diplomacy/web/src/gui/maps/common/supportMove.js create mode 100644 diplomacy/web/src/gui/maps/common/unit.js create mode 100644 diplomacy/web/src/gui/maps/modern/SvgModern.css create mode 100644 diplomacy/web/src/gui/maps/modern/SvgModern.js create mode 100644 diplomacy/web/src/gui/maps/modern/SvgModernMetadata.js create mode 100644 diplomacy/web/src/gui/maps/pure/SvgPure.css create mode 100644 diplomacy/web/src/gui/maps/pure/SvgPure.js create mode 100644 diplomacy/web/src/gui/maps/pure/SvgPureMetadata.js delete mode 100644 diplomacy/web/src/gui/maps/standard/SvgStandardExtra.js create mode 100644 diplomacy/web/src/gui/maps/standard/SvgStandardMetadata.js delete mode 100644 diplomacy/web/src/gui/maps/standard/build.js delete mode 100644 diplomacy/web/src/gui/maps/standard/common.js delete mode 100644 diplomacy/web/src/gui/maps/standard/convoy.js delete mode 100644 diplomacy/web/src/gui/maps/standard/disband.js delete mode 100644 diplomacy/web/src/gui/maps/standard/hold.js delete mode 100644 diplomacy/web/src/gui/maps/standard/move.js delete mode 100644 diplomacy/web/src/gui/maps/standard/supplyCenter.js delete mode 100644 diplomacy/web/src/gui/maps/standard/supportHold.js delete mode 100644 diplomacy/web/src/gui/maps/standard/supportMove.js delete mode 100644 diplomacy/web/src/gui/maps/standard/unit.js create mode 100644 diplomacy/web/src/gui/wizards/gameCreation/gameCreationWizard.css diff --git a/diplomacy/engine/renderer.py b/diplomacy/engine/renderer.py index c6369c4..01fe209 100644 --- a/diplomacy/engine/renderer.py +++ b/diplomacy/engine/renderer.py @@ -20,10 +20,11 @@ """ import os from xml.dom import minidom +from typing import Tuple from diplomacy import settings +from diplomacy.utils.equilateral_triangle import EquilateralTriangle # Constants -LAYER_SC = 'SupplyCenterLayer' LAYER_ORDER = 'OrderLayer' LAYER_UNIT = 'UnitLayer' LAYER_DISL = 'DislodgedUnitLayer' @@ -34,11 +35,6 @@ def _attr(node_element, attr_name): """ Shorthand method to retrieve an XML attribute """ return node_element.attributes[attr_name].value -def _offset(str_float, offset): - """ Shorthand to add a offset to an attribute """ - return str(float(str_float) + offset) - - class Renderer(): """ Renderer object responsible for rendering a game state to svg """ @@ -87,7 +83,6 @@ class Renderer(): # Parsing XML xml_map = minidom.parseString(self.xml_map) - scs = self.game.map.scs[:] # Setting phase and note nb_centers = [(power.name[:3], len(power.centers)) @@ -98,16 +93,14 @@ class Renderer(): xml_map = self._set_current_phase(xml_map, self.game.get_current_phase()) xml_map = self._set_note(xml_map, nb_centers_per_power, self.game.note) - # Adding units, supply centers, and influence + # Adding units and influence for power in self.game.powers.values(): for unit in power.units: xml_map = self._add_unit(xml_map, unit, power.name, is_dislodged=False) for unit in power.retreats: xml_map = self._add_unit(xml_map, unit, power.name, is_dislodged=True) for center in power.centers: - xml_map = self._add_supply_center(xml_map, center, power.name) xml_map = self._set_influence(xml_map, center, power.name, has_supply_center=True) - scs.remove(center) for loc in power.influence: xml_map = self._set_influence(xml_map, loc, power.name, has_supply_center=False) @@ -179,10 +172,6 @@ class Renderer(): else: raise RuntimeError('Unknown order: {}'.format(order)) - # Adding remaining supply centers - for center in scs: - xml_map = self._add_supply_center(xml_map, center, None) - # Removing abbrev and mouse layer svg_node = xml_map.getElementsByTagName('svg')[0] for child_node in svg_node.childNodes: @@ -225,15 +214,6 @@ class Renderer(): self.metadata['symbol_size'][_attr(child_node, 'name')] = (_attr(child_node, 'height'), _attr(child_node, 'width')) - # Order type - elif child_node.nodeName.startswith('jdipNS'): - order_type = child_node.nodeName.replace('jdipNS:', '') - self.metadata['orders'][order_type] = {} - for attr_name, attr_value in child_node.attributes.items(): - if ':' in attr_name: - continue - self.metadata['orders'][order_type][attr_name] = attr_value - # Object coordinates for province_data in xml_map.getElementsByTagName('jdipNS:PROVINCE_DATA'): for child_node in province_data.childNodes: @@ -248,8 +228,6 @@ class Renderer(): self.metadata['coord'][province]['unit'] = (_attr(coord_node, 'x'), _attr(coord_node, 'y')) elif coord_node.nodeName == 'jdipNS:DISLODGED_UNIT': self.metadata['coord'][province]['disl'] = (_attr(coord_node, 'x'), _attr(coord_node, 'y')) - elif coord_node.nodeName == 'jdipNS:SUPPLY_CENTER': - self.metadata['coord'][province]['sc'] = (_attr(coord_node, 'x'), _attr(coord_node, 'y')) # Deleting svg_node = xml_map.getElementsByTagName('svg')[0] @@ -268,9 +246,10 @@ class Renderer(): """ unit_type, loc = unit.split() symbol = FLEET if unit_type == 'F' else ARMY - loc_x = _offset(self.metadata['coord'][loc][('unit', 'disl')[is_dislodged]][0], -11.5) - loc_y = _offset(self.metadata['coord'][loc][('unit', 'disl')[is_dislodged]][1], - 10.) + loc_x = self.metadata['coord'][loc][('unit', 'disl')[is_dislodged]][0] + loc_y = self.metadata['coord'][loc][('unit', 'disl')[is_dislodged]][1] node = xml_map.createElement('use') + node.setAttribute('id', '%sunit_%s' % ('dislodged_' if is_dislodged else '', loc)) node.setAttribute('x', loc_x) node.setAttribute('y', loc_y) node.setAttribute('height', self.metadata['symbol_size'][symbol][0]) @@ -286,34 +265,6 @@ class Renderer(): break return xml_map - def _add_supply_center(self, xml_map, loc, power_name): - """ Adds a supply center to the map - :param xml_map: The xml map being generated - :param loc: The province where to add the SC (e.g. 'PAR') - :param power_name: The name of the power owning the SC or None - :return: Nothing - """ - symbol = 'SupplyCenter' - loc_x = _offset(self.metadata['coord'][loc]['sc'][0], -8.5) - loc_y = _offset(self.metadata['coord'][loc]['sc'][1], -11.) - node = xml_map.createElement('use') - node.setAttribute('x', loc_x) - node.setAttribute('y', loc_y) - node.setAttribute('height', self.metadata['symbol_size'][symbol][0]) - node.setAttribute('width', self.metadata['symbol_size'][symbol][1]) - node.setAttribute('xlink:href', '#{}'.format(symbol)) - if power_name: - node.setAttribute('class', 'sc{}'.format(power_name.lower())) - else: - node.setAttribute('class', 'scnopower') - - # Inserting - for child_node in xml_map.getElementsByTagName('svg')[0].childNodes: - if child_node.nodeName == 'g' and _attr(child_node, 'id') == 'SupplyCenterLayer': - child_node.appendChild(node) - break - return xml_map - def _set_influence(self, xml_map, loc, power_name, has_supply_center=False): """ Sets the influence on the map :param xml_map: The xml map being generated @@ -325,18 +276,35 @@ class Renderer(): loc = loc.upper()[:3] if loc in self.game.map.scs and not has_supply_center: return xml_map - if self.game.map.area_type(loc) not in ['LAND', 'COAST']: + if self.game.map.area_type(loc) == 'WATER': return xml_map + class_name = power_name.lower() if power_name else 'nopower' + # Inserting + map_layer = None for child_node in xml_map.getElementsByTagName('svg')[0].childNodes: if child_node.nodeName == 'g' and _attr(child_node, 'id') == 'MapLayer': - for map_node in child_node.childNodes: - if map_node.nodeName == 'path' and _attr(map_node, 'id') == '_{}'.format(loc.lower()): - if power_name: - map_node.setAttribute('class', power_name.lower()) - else: - map_node.setAttribute('class', 'nopower') + map_layer = child_node + break + + if map_layer: + for map_node in map_layer.childNodes: + if (map_node.nodeName in ('g', 'path', 'polygon') + and map_node.getAttribute('id') == '_{}'.format(loc.lower())): + + # Province is a polygon - Setting influence directly + if map_node.nodeName in ('path', 'polygon'): + map_node.setAttribute('class', class_name) + return xml_map + + # Otherwise, map node is a 'g' node. + node_edited = False + for sub_node in map_node.childNodes: + if sub_node.nodeName in ('path', 'polygon') and sub_node.getAttribute('class') != 'water': + node_edited = True + sub_node.setAttribute('class', class_name) + if node_edited: return xml_map # Returning @@ -349,7 +317,7 @@ class Renderer(): :param current_phase: The current phase (e.g. 'S1901M) :return: Nothing """ - current_phase = 'FINAL' if current_phase[0] == '?' else current_phase + current_phase = 'FINAL' if current_phase[0] == '?' or current_phase == 'COMPLETED' else current_phase for child_node in xml_map.getElementsByTagName('svg')[0].childNodes: if child_node.nodeName == 'text' and _attr(child_node, 'id') == 'CurrentPhase': child_node.childNodes[0].nodeValue = current_phase @@ -380,32 +348,22 @@ class Renderer(): :param power_name: The name of the power owning the unit :return: Nothing """ - # Calculating polygon coord - polygon_coord = [] - loc_x = _offset(self.metadata['coord'][loc]['unit'][0], 8.5) - loc_y = _offset(self.metadata['coord'][loc]['unit'][1], 9.5) - for offset in [(13.8, -33.3), (33.3, -13.8), (33.3, 13.8), (13.8, 33.3), (-13.8, 33.3), (-33.3, 13.8), - (-33.3, -13.8), (-13.8, -33.3)]: - polygon_coord += [_offset(loc_x, offset[0]) + ',' + _offset(loc_y, offset[1])] - - # Building polygon - g_node = xml_map.createElement('g') - - poly_1 = xml_map.createElement('polygon') - poly_1.setAttribute('stroke-width', '10') - poly_1.setAttribute('class', 'varwidthshadow') - poly_1.setAttribute('points', ' '.join(polygon_coord)) - - poly_2 = xml_map.createElement('polygon') - poly_2.setAttribute('stroke-width', '6') - poly_2.setAttribute('class', 'varwidthorder') - poly_2.setAttribute('points', ' '.join(polygon_coord)) - poly_2.setAttribute('stroke', self.metadata['color'][power_name]) + # Symbols + symbol = 'HoldUnit' + loc_x, loc_y = self._center_symbol_around_unit(loc, False, symbol) - g_node.appendChild(poly_1) - g_node.appendChild(poly_2) + # Creating nodes + g_node = xml_map.createElement('g') + g_node.setAttribute('stroke', self.metadata['color'][power_name]) + symbol_node = xml_map.createElement('use') + symbol_node.setAttribute('x', loc_x) + symbol_node.setAttribute('y', loc_y) + symbol_node.setAttribute('height', self.metadata['symbol_size'][symbol][0]) + symbol_node.setAttribute('width', self.metadata['symbol_size'][symbol][1]) + symbol_node.setAttribute('xlink:href', '#{}'.format(symbol)) # Inserting + g_node.appendChild(symbol_node) for child_node in xml_map.getElementsByTagName('svg')[0].childNodes: if child_node.nodeName == 'g' and _attr(child_node, 'id') == 'OrderLayer': for layer_node in child_node.childNodes: @@ -424,58 +382,49 @@ class Renderer(): :param power_name: The power name issuing the move order :return: Nothing """ - loc_x = _offset(self.metadata['coord'][loc]['unit'][0], 10) - loc_y = _offset(self.metadata['coord'][loc]['unit'][1], 10) - dest_loc_x = _offset(self.metadata['coord'][dest_loc]['unit'][0], 10) - dest_loc_y = _offset(self.metadata['coord'][dest_loc]['unit'][1], 10) + # Symbols + symbol = 'SupportHoldUnit' + symbol_loc_x, symbol_loc_y = self._center_symbol_around_unit(dest_loc, False, symbol) + symbol_node = xml_map.createElement('use') + symbol_node.setAttribute('x', symbol_loc_x) + symbol_node.setAttribute('y', symbol_loc_y) + symbol_node.setAttribute('height', self.metadata['symbol_size'][symbol][0]) + symbol_node.setAttribute('width', self.metadata['symbol_size'][symbol][1]) + symbol_node.setAttribute('xlink:href', '#{}'.format(symbol)) + + loc_x, loc_y = self._get_unit_center(loc, False) + dest_loc_x, dest_loc_y = self._get_unit_center(dest_loc, False) # Adjusting destination - delta_x = float(dest_loc_x) - float(loc_x) - delta_y = float(dest_loc_y) - float(loc_y) - vector_length = (delta_x ** 2. + delta_y ** 2.) ** 0.5 - dest_loc_x = str(round(float(loc_x) + (vector_length - 35.) / vector_length * delta_x, 2)) - dest_loc_y = str(round(float(loc_y) + (vector_length - 35.) / vector_length * delta_y, 2)) - - # Getting polygon coordinates - polygon_coord = [] - poly_loc_x = _offset(self.metadata['coord'][dest_loc]['unit'][0], 8.5) - poly_loc_y = _offset(self.metadata['coord'][dest_loc]['unit'][1], 9.5) - for offset in [(15.9, -38.3), (38.3, -15.9), (38.3, 15.9), (15.9, 38.3), (-15.9, 38.3), (-38.3, 15.9), - (-38.3, -15.9), (-15.9, -38.3)]: - polygon_coord += [_offset(poly_loc_x, offset[0]) + ',' + _offset(poly_loc_y, offset[1])] + delta_x = dest_loc_x - loc_x + delta_y = dest_loc_y - loc_y + vector_length = (delta_x ** 2 + delta_y ** 2) ** 0.5 + delta_dec = float(self.metadata['symbol_size'][symbol][1]) / 2 + dest_loc_x = round(loc_x + (vector_length - delta_dec) / vector_length * delta_x, 2) + dest_loc_y = round(loc_y + (vector_length - delta_dec) / vector_length * delta_y, 2) # Creating nodes g_node = xml_map.createElement('g') + g_node.setAttribute('stroke', self.metadata['color'][power_name]) shadow_line = xml_map.createElement('line') - shadow_line.setAttribute('x1', loc_x) - shadow_line.setAttribute('y1', loc_y) - shadow_line.setAttribute('x2', dest_loc_x) - shadow_line.setAttribute('y2', dest_loc_y) + shadow_line.setAttribute('x1', str(loc_x)) + shadow_line.setAttribute('y1', str(loc_y)) + shadow_line.setAttribute('x2', str(dest_loc_x)) + shadow_line.setAttribute('y2', str(dest_loc_y)) shadow_line.setAttribute('class', 'shadowdash') support_line = xml_map.createElement('line') - support_line.setAttribute('x1', loc_x) - support_line.setAttribute('y1', loc_y) - support_line.setAttribute('x2', dest_loc_x) - support_line.setAttribute('y2', dest_loc_y) + support_line.setAttribute('x1', str(loc_x)) + support_line.setAttribute('y1', str(loc_y)) + support_line.setAttribute('x2', str(dest_loc_x)) + support_line.setAttribute('y2', str(dest_loc_y)) support_line.setAttribute('class', 'supportorder') - support_line.setAttribute('stroke', self.metadata['color'][power_name]) - - shadow_poly = xml_map.createElement('polygon') - shadow_poly.setAttribute('class', 'shadowdash') - shadow_poly.setAttribute('points', ' '.join(polygon_coord)) - - support_poly = xml_map.createElement('polygon') - support_poly.setAttribute('class', 'supportorder') - support_poly.setAttribute('points', ' '.join(polygon_coord)) - support_poly.setAttribute('stroke', self.metadata['color'][power_name]) # Inserting g_node.appendChild(shadow_line) g_node.appendChild(support_line) - g_node.appendChild(shadow_poly) - g_node.appendChild(support_poly) + g_node.appendChild(symbol_node) for child_node in xml_map.getElementsByTagName('svg')[0].childNodes: if child_node.nodeName == 'g' and _attr(child_node, 'id') == 'OrderLayer': @@ -495,21 +444,22 @@ class Renderer(): :param power_name: The power name issuing the move order :return: Nothing """ - if self.game.get_current_phase()[-1] == 'R': - src_loc_x = _offset(self.metadata['coord'][src_loc]['unit'][0], -2.5) - src_loc_y = _offset(self.metadata['coord'][src_loc]['unit'][1], -2.5) - else: - src_loc_x = _offset(self.metadata['coord'][src_loc]['unit'][0], 10) - src_loc_y = _offset(self.metadata['coord'][src_loc]['unit'][1], 10) - dest_loc_x = _offset(self.metadata['coord'][dest_loc]['unit'][0], 10) - dest_loc_y = _offset(self.metadata['coord'][dest_loc]['unit'][1], 10) + is_dislodged = self.game.get_current_phase()[-1] == 'R' + src_loc_x, src_loc_y = self._get_unit_center(src_loc, is_dislodged) + dest_loc_x, dest_loc_y = self._get_unit_center(dest_loc, is_dislodged) # Adjusting destination - delta_x = float(dest_loc_x) - float(src_loc_x) - delta_y = float(dest_loc_y) - float(src_loc_y) - vector_length = (delta_x ** 2. + delta_y ** 2.) ** 0.5 - dest_loc_x = str(round(float(src_loc_x) + (vector_length - 30.) / vector_length * delta_x, 2)) - dest_loc_y = str(round(float(src_loc_y) + (vector_length - 30.) / vector_length * delta_y, 2)) + delta_x = dest_loc_x - src_loc_x + delta_y = dest_loc_y - src_loc_y + vector_length = (delta_x ** 2 + delta_y ** 2) ** 0.5 + delta_dec = float(self.metadata['symbol_size'][ARMY][1]) / 2 + 2 * self._colored_stroke_width() + dest_loc_x = str(round(src_loc_x + (vector_length - delta_dec) / vector_length * delta_x, 2)) + dest_loc_y = str(round(src_loc_y + (vector_length - delta_dec) / vector_length * delta_y, 2)) + + src_loc_x = str(src_loc_x) + src_loc_y = str(src_loc_y) + dest_loc_x = str(dest_loc_x) + dest_loc_y = str(dest_loc_y) # Creating nodes g_node = xml_map.createElement('g') @@ -520,7 +470,7 @@ class Renderer(): line_with_shadow.setAttribute('x2', dest_loc_x) line_with_shadow.setAttribute('y2', dest_loc_y) line_with_shadow.setAttribute('class', 'varwidthshadow') - line_with_shadow.setAttribute('stroke-width', '10') + line_with_shadow.setAttribute('stroke-width', str(self._plain_stroke_width())) line_with_arrow = xml_map.createElement('line') line_with_arrow.setAttribute('x1', src_loc_x) @@ -529,7 +479,7 @@ class Renderer(): line_with_arrow.setAttribute('y2', dest_loc_y) line_with_arrow.setAttribute('class', 'varwidthorder') line_with_arrow.setAttribute('stroke', self.metadata['color'][power_name]) - line_with_arrow.setAttribute('stroke-width', '6') + line_with_arrow.setAttribute('stroke-width', str(self._colored_stroke_width())) line_with_arrow.setAttribute('marker-end', 'url(#arrow)') # Inserting @@ -554,19 +504,17 @@ class Renderer(): :param power_name: The power name issuing the move order :return: Nothing """ - loc_x = _offset(self.metadata['coord'][loc]['unit'][0], 10) - loc_y = _offset(self.metadata['coord'][loc]['unit'][1], 10) - src_loc_x = _offset(self.metadata['coord'][src_loc]['unit'][0], 10) - src_loc_y = _offset(self.metadata['coord'][src_loc]['unit'][1], 10) - dest_loc_x = _offset(self.metadata['coord'][dest_loc]['unit'][0], 10) - dest_loc_y = _offset(self.metadata['coord'][dest_loc]['unit'][1], 10) + loc_x, loc_y = self._get_unit_center(loc, False) + src_loc_x, src_loc_y = self._get_unit_center(src_loc, False) + dest_loc_x, dest_loc_y = self._get_unit_center(dest_loc, False) # Adjusting destination - delta_x = float(dest_loc_x) - float(src_loc_x) - delta_y = float(dest_loc_y) - float(src_loc_y) - vector_length = (delta_x ** 2. + delta_y ** 2.) ** 0.5 - dest_loc_x = str(round(float(src_loc_x) + (vector_length - 30.) / vector_length * delta_x, 2)) - dest_loc_y = str(round(float(src_loc_y) + (vector_length - 30.) / vector_length * delta_y, 2)) + delta_x = dest_loc_x - src_loc_x + delta_y = dest_loc_y - src_loc_y + vector_length = (delta_x ** 2 + delta_y ** 2) ** 0.5 + delta_dec = float(self.metadata['symbol_size'][ARMY][1]) / 2 + 2 * self._colored_stroke_width() + dest_loc_x = str(round(src_loc_x + (vector_length - delta_dec) / vector_length * delta_x, 2)) + dest_loc_y = str(round(src_loc_y + (vector_length - delta_dec) / vector_length * delta_y, 2)) # Creating nodes g_node = xml_map.createElement('g') @@ -615,46 +563,57 @@ class Renderer(): :param power_name: The power name issuing the convoy order :return: Nothing """ - loc_x = _offset(self.metadata['coord'][loc]['unit'][0], 10) - loc_y = _offset(self.metadata['coord'][loc]['unit'][1], 10) - src_loc_x = _offset(self.metadata['coord'][src_loc]['unit'][0], 10) - src_loc_y = _offset(self.metadata['coord'][src_loc]['unit'][1], 10) - dest_loc_x = _offset(self.metadata['coord'][dest_loc]['unit'][0], 10) - dest_loc_y = _offset(self.metadata['coord'][dest_loc]['unit'][1], 10) + symbol = 'ConvoyTriangle' + symbol_loc_x, symbol_loc_y = self._center_symbol_around_unit(src_loc, False, symbol) + symbol_height = float(self.metadata['symbol_size'][symbol][0]) + symbol_width = float(self.metadata['symbol_size'][symbol][1]) + triangle = EquilateralTriangle(x_top=float(symbol_loc_x) + symbol_width / 2, + y_top=float(symbol_loc_y), + x_right=float(symbol_loc_x) + symbol_width, + y_right=float(symbol_loc_y) + symbol_height, + x_left=float(symbol_loc_x), + y_left=float(symbol_loc_y) + symbol_height) + symbol_loc_y = str(float(symbol_loc_y) - float(self.metadata['symbol_size'][symbol][0]) / 6) + + loc_x, loc_y = self._get_unit_center(loc, False) + src_loc_x, src_loc_y = self._get_unit_center(src_loc, False) + dest_loc_x, dest_loc_y = self._get_unit_center(dest_loc, False) # Adjusting starting arrow (from convoy to start location) # This is to avoid the end of the arrow conflicting with the convoy triangle - src_delta_x = float(src_loc_x) - float(loc_x) - src_delta_y = float(src_loc_y) - float(loc_y) - src_vector_length = (src_delta_x ** 2. + src_delta_y ** 2.) ** 0.5 - src_loc_x_1 = str(round(float(loc_x) + (src_vector_length - 30.) / src_vector_length * src_delta_x, 2)) - src_loc_y_1 = str(round(float(loc_y) + (src_vector_length - 30.) / src_vector_length * src_delta_y, 2)) + src_loc_x_1, src_loc_y_1 = triangle.intersection(loc_x, loc_y) + src_loc_x_1 = str(src_loc_x_1) + src_loc_y_1 = str(src_loc_y_1) # Adjusting destination arrow (from start location to destination location) # This is to avoid the start of the arrow conflicting with the convoy triangle - dest_delta_x = float(src_loc_x) - float(dest_loc_x) - dest_delta_y = float(src_loc_y) - float(dest_loc_y) - dest_vector_length = (dest_delta_x ** 2. + dest_delta_y ** 2.) ** 0.5 - src_loc_x_2 = str(round(float(dest_loc_x) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_x, 2)) - src_loc_y_2 = str(round(float(dest_loc_y) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_y, 2)) + src_loc_x_2, src_loc_y_2 = triangle.intersection(dest_loc_x, dest_loc_y) + src_loc_x_2 = str(src_loc_x_2) + src_loc_y_2 = str(src_loc_y_2) # Adjusting destination arrow (from start location to destination location) # This is to avoid the start of the arrow conflicting with the convoy triangle - dest_delta_x = float(dest_loc_x) - float(src_loc_x) - dest_delta_y = float(dest_loc_y) - float(src_loc_y) - dest_vector_length = (dest_delta_x ** 2. + dest_delta_y ** 2.) ** 0.5 - dest_loc_x = str(round(float(src_loc_x) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_x, 2)) - dest_loc_y = str(round(float(src_loc_y) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_y, 2)) - - # Getting convoy triangle coordinates - triangle_coord = [] - triangle_loc_x = _offset(self.metadata['coord'][src_loc]['unit'][0], 10) - triangle_loc_y = _offset(self.metadata['coord'][src_loc]['unit'][1], 10) - for offset in [(0, -38.3), (33.2, 19.1), (-33.2, 19.1)]: - triangle_coord += [_offset(triangle_loc_x, offset[0]) + ',' + _offset(triangle_loc_y, offset[1])] + dest_delta_x = dest_loc_x - src_loc_x + dest_delta_y = dest_loc_y - src_loc_y + dest_vector_length = (dest_delta_x ** 2 + dest_delta_y ** 2) ** 0.5 + delta_dec = float(self.metadata['symbol_size'][ARMY][1]) / 2 + 2 * self._colored_stroke_width() + dest_loc_x = str(round(src_loc_x + (dest_vector_length - delta_dec) / dest_vector_length * dest_delta_x, 2)) + dest_loc_y = str(round(src_loc_y + (dest_vector_length - delta_dec) / dest_vector_length * dest_delta_y, 2)) + + loc_x = str(loc_x) + loc_y = str(loc_y) + + # Generating convoy triangle node + symbol_node = xml_map.createElement('use') + symbol_node.setAttribute('x', symbol_loc_x) + symbol_node.setAttribute('y', symbol_loc_y) + symbol_node.setAttribute('height', self.metadata['symbol_size'][symbol][0]) + symbol_node.setAttribute('width', self.metadata['symbol_size'][symbol][1]) + symbol_node.setAttribute('xlink:href', '#{}'.format(symbol)) # Creating nodes g_node = xml_map.createElement('g') + g_node.setAttribute('stroke', self.metadata['color'][power_name]) src_shadow_line = xml_map.createElement('line') src_shadow_line.setAttribute('x1', loc_x) @@ -663,20 +622,19 @@ class Renderer(): src_shadow_line.setAttribute('y2', src_loc_y_1) src_shadow_line.setAttribute('class', 'shadowdash') - dest_shadow_line = xml_map.createElement('line') - dest_shadow_line.setAttribute('x1', src_loc_x_2) - dest_shadow_line.setAttribute('y1', src_loc_y_2) - dest_shadow_line.setAttribute('x2', dest_loc_x) - dest_shadow_line.setAttribute('y2', dest_loc_y) - dest_shadow_line.setAttribute('class', 'shadowdash') - src_convoy_line = xml_map.createElement('line') src_convoy_line.setAttribute('x1', loc_x) src_convoy_line.setAttribute('y1', loc_y) src_convoy_line.setAttribute('x2', src_loc_x_1) src_convoy_line.setAttribute('y2', src_loc_y_1) src_convoy_line.setAttribute('class', 'convoyorder') - src_convoy_line.setAttribute('stroke', self.metadata['color'][power_name]) + + dest_shadow_line = xml_map.createElement('line') + dest_shadow_line.setAttribute('x1', src_loc_x_2) + dest_shadow_line.setAttribute('y1', src_loc_y_2) + dest_shadow_line.setAttribute('x2', dest_loc_x) + dest_shadow_line.setAttribute('y2', dest_loc_y) + dest_shadow_line.setAttribute('class', 'shadowdash') dest_convoy_line = xml_map.createElement('line') dest_convoy_line.setAttribute('x1', src_loc_x_2) @@ -684,25 +642,14 @@ class Renderer(): dest_convoy_line.setAttribute('x2', dest_loc_x) dest_convoy_line.setAttribute('y2', dest_loc_y) dest_convoy_line.setAttribute('class', 'convoyorder') - dest_convoy_line.setAttribute('stroke', self.metadata['color'][power_name]) dest_convoy_line.setAttribute('marker-end', 'url(#arrow)') - shadow_poly = xml_map.createElement('polygon') - shadow_poly.setAttribute('class', 'shadowdash') - shadow_poly.setAttribute('points', ' '.join(triangle_coord)) - - convoy_poly = xml_map.createElement('polygon') - convoy_poly.setAttribute('class', 'convoyorder') - convoy_poly.setAttribute('points', ' '.join(triangle_coord)) - convoy_poly.setAttribute('stroke', self.metadata['color'][power_name]) - # Inserting g_node.appendChild(src_shadow_line) g_node.appendChild(dest_shadow_line) g_node.appendChild(src_convoy_line) g_node.appendChild(dest_convoy_line) - g_node.appendChild(shadow_poly) - g_node.appendChild(convoy_poly) + g_node.appendChild(symbol_node) for child_node in xml_map.getElementsByTagName('svg')[0].childNodes: if child_node.nodeName == 'g' and _attr(child_node, 'id') == 'OrderLayer': for layer_node in child_node.childNodes: @@ -721,15 +668,14 @@ class Renderer(): :param power_name: The name of the power building the unit :return: Nothing """ - loc_x = _offset(self.metadata['coord'][loc]['unit'][0], -11.5) - loc_y = _offset(self.metadata['coord'][loc]['unit'][1], - 10.) - build_loc_x = _offset(self.metadata['coord'][loc]['unit'][0], -20.5) - build_loc_y = _offset(self.metadata['coord'][loc]['unit'][1], -20.5) - # Symbols symbol = ARMY if unit_type == 'A' else FLEET build_symbol = 'BuildUnit' + loc_x = self.metadata['coord'][loc]['unit'][0] + loc_y = self.metadata['coord'][loc]['unit'][1] + build_loc_x, build_loc_y = self._center_symbol_around_unit(loc, False, build_symbol) + # Creating nodes g_node = xml_map.createElement('g') @@ -765,15 +711,9 @@ class Renderer(): :param loc: The province where the unit is disbanded (e.g. 'PAR') :return: Nothing """ - if self.game.get_current_phase()[-1] == 'R': - loc_x = _offset(self.metadata['coord'][loc]['unit'][0], -29.) - loc_y = _offset(self.metadata['coord'][loc]['unit'][1], -27.5) - else: - loc_x = _offset(self.metadata['coord'][loc]['unit'][0], -16.5) - loc_y = _offset(self.metadata['coord'][loc]['unit'][1], -15.) - # Symbols symbol = 'RemoveUnit' + loc_x, loc_y = self._center_symbol_around_unit(loc, self.game.get_current_phase()[-1] == 'R', symbol) # Creating nodes g_node = xml_map.createElement('g') @@ -793,3 +733,44 @@ class Renderer(): # Returning return xml_map + + def _center_symbol_around_unit(self, loc, is_dislodged, symbol): # type: (str, bool, str) -> Tuple[str, str] + """ Compute top-left coordinates of a symbol to be centered around a unit. + :param loc: unit location (e.g. 'PAR') + :param is_dislodged: boolean to tell if unit is dislodged + :param symbol: symbol identifier (e.g. 'HoldUnit') + :return: a couple of coordinates (x, y) as string values + """ + key = 'disl' if is_dislodged else 'unit' + unit_x, unit_y = self.metadata['coord'][loc][key] + unit_height, unit_width = self.metadata['symbol_size'][ARMY] + symbol_height, symbol_width = self.metadata['symbol_size'][symbol] + return ( + str(float(unit_x) + float(unit_width) / 2 - float(symbol_width) / 2), + str(float(unit_y) + float(unit_height) / 2 - float(symbol_height) / 2) + ) + + def _get_unit_center(self, loc, is_dislodged): # type: (str, bool) -> Tuple[float, float] + """ Compute coordinates of unit center. + :param loc: unit location + :param is_dislodged: boolean to tell if unit is dislodged + :return: a couple of coordinates (x, y) as floating values + """ + unit_x, unit_y = self.metadata['coord'][loc]['disl' if is_dislodged else 'unit'] + unit_height, unit_width = self.metadata['symbol_size'][ARMY] + return ( + float(unit_x) + float(unit_width) / 2, + float(unit_y) + float(unit_height) / 2 + ) + + def _plain_stroke_width(self): # type: () -> float + """ Return generic stroke width for plain lines. + :return: stroke width as floating value. + """ + return float(self.metadata['symbol_size']['Stroke'][0]) + + def _colored_stroke_width(self): # type: () -> float + """ Return generic stroke width for colored or textured lines. + :return: stroke width as floating value. + """ + return float(self.metadata['symbol_size']['Stroke'][1]) diff --git a/diplomacy/integration/webdiplomacy_net/api.py b/diplomacy/integration/webdiplomacy_net/api.py index 200487d..a12e28a 100644 --- a/diplomacy/integration/webdiplomacy_net/api.py +++ b/diplomacy/integration/webdiplomacy_net/api.py @@ -18,9 +18,9 @@ import logging import os from urllib.parse import urlencode -import ujson as json from tornado import gen from tornado.httpclient import HTTPRequest +import ujson as json from diplomacy.integration.base_api import BaseAPI from diplomacy.integration.webdiplomacy_net.game import state_dict_to_game_and_power from diplomacy.integration.webdiplomacy_net.orders import Order diff --git a/diplomacy/maps/svg/ancmed.svg b/diplomacy/maps/svg/ancmed.svg index b94a89e..2d238ed 100644 --- a/diplomacy/maps/svg/ancmed.svg +++ b/diplomacy/maps/svg/ancmed.svg @@ -1,483 +1,422 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Ancient Med Map - - - - - ]]> - + + @@ -493,15 +432,15 @@ - + - + - + @@ -510,14 +449,15 @@ - - + + + @@ -525,19 +465,40 @@ - + - + + + + + + + + + + + + + + + + + + + + + + - - - + + + @@ -551,9 +512,9 @@ - - - + + + @@ -568,9 +529,9 @@ - - - + + + @@ -584,9 +545,9 @@ - - - + + + @@ -602,151 +563,186 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -762,192 +758,193 @@ - - - - adr - aeg - ale - ant - apu - ara - arm - ath - aus - bal - bay - ber - bit - bla - byz - cap - car - che - cil - cir - cor - cre - cyp - cyr - dac - dal - dam - egy - epi - etr - gal - gau - gop - gos - got - ibe - ill - ion - isa - jer - lep - lib - lig - lus - mac - mar - mas - mau - mem - mes - mil - min - nab - nea - num - pet - pha - pun - rav - ree - rha - rom - sag - sah - sad - sam - sic - sid - sin - sip - spa - syr - tar - tha - thb - tye - tyn - ven - vin - - - - - - - - - S1901M - - - - + + + + adr + aeg + ale + ant + apu + ara + arm + ath + aus + bal + bay + ber + bit + bla + byz + cap + car + che + cil + cir + cor + cre + cyp + cyr + dac + dal + dam + egy + epi + etr + gal + gau + gop + gos + got + ibe + ill + ion + isa + jer + lep + lib + lig + lus + mac + mar + mas + mau + mem + mes + mil + min + nab + nea + num + pet + pha + pun + rav + ree + rha + rom + sag + sah + sad + sam + sic + sid + sin + sip + spa + syr + tar + tha + thb + tye + tyn + ven + vin + + + + + + + + + + S1901M + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/diplomacy/maps/svg/modern.svg b/diplomacy/maps/svg/modern.svg index 2701137..8d3efe7 100644 --- a/diplomacy/maps/svg/modern.svg +++ b/diplomacy/maps/svg/modern.svg @@ -1,787 +1,694 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MODERN - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MODERN + + + + + - .symDarkener {fill:black;opacity:0.45;fill-opacity:0.45;} - .symCenterHub {fill:black; stroke:black; opacity:0.60; stroke-width:0.5px;} - .symBorder {stroke:black;stroke-width:3%;} - .symThinBorder {stroke:black;stroke-width:0.4;} - .symSilhouette {stroke:black;fill:black;stroke-width:1;} - - ]]> - - - + + + @@ -797,15 +704,15 @@ - + - + - + @@ -814,14 +721,15 @@ - - + + + @@ -829,19 +737,40 @@ - + - + + + + + + + + + + + + + + + + + + + + + + - - - + + + @@ -855,9 +784,9 @@ - - - + + + @@ -872,9 +801,9 @@ - - - + + + @@ -888,9 +817,9 @@ - - - + + + @@ -906,209 +835,274 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - nc - sc - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nc + sc + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1124,323 +1118,324 @@ - - - Ada - ADR - AEG - Alb - Ale - Alg - Als - Ana - Adl - Ank - Apu - ARA - ARC - Arm - Asw - Aus - Auv - Aze - BAL - Bar - Bel - Ber - BHM - Bie - BIS - Bor - Bos - Bri - BRN - Bul - Cai - CAS - Cau - Crp - Cly - Cro - Cze - Den - Don - EBS - Edi - EME - ENG - Esa - Est - Fin - Fra - Gda - Geo - Gib - GOB - GOL - Gor - Gre - Ham - HEL - Hol - Hun - Ice - ION - Ire - IRI - Irk - Irn - Isr - Ist - Izm - Jor - Kaz - Kha - Kie - Kra - Lap - Lat - LBN - Lib - LIG - Lit - Lpl - Lon - Lyo - Mac - Mad - MAL - Mar - MAT - Mil - Mol - Mon - Mor - Mos - Mun - Mur - Nap - NAT - Nav - Nwy - NTH - NWG - Ode - Par - PER - Pic - Pie - Pod - Por - Pru - RED - Rom - Ros - Ruh - Rum - SAT - Sau - Sax - Ser - Sev - Sib - Sil - Sin - SKA - Slk - Stp - SOG - Svl - Swe - Swi - Syr - Tun - Tus - TYR - Ura - Ven - Vol - Wal - War - WBS - WME - Wsa - WHI - Yor - - - + + ada + adr + aeg + alb + ale + alg + als + ana + adl + ank + apu + ara + arc + arm + asw + aus + auv + aze + bal + bar + bel + ber + bhm + bie + bis + bor + bos + bri + brn + bul + cai + cas + cau + crp + cly + cro + cze + den + don + ebs + edi + eme + eng + esa + est + fin + fra + gda + geo + gib + gob + gol + gor + gre + ham + hel + hol + hun + ice + ion + ire + iri + irk + irn + isr + ist + izm + jor + kaz + kha + kie + kra + lap + lat + lbn + lib + lig + lit + lpl + lon + lyo + mac + mad + mal + mar + mat + mil + mol + mon + mor + mos + mun + mur + nap + nat + nav + nwy + nth + nwg + ode + par + per + pic + pie + pod + por + pru + red + rom + ros + ruh + rum + sat + sau + sax + ser + sev + sib + sil + sin + ska + slk + stp + sog + svl + swe + swi + syr + tun + tus + tyr + ura + ven + vol + wal + war + wbs + wme + wsa + whi + yor + + + + - - - - S1901M - - + + + + + S1901M + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/diplomacy/maps/svg/pure.svg b/diplomacy/maps/svg/pure.svg index bedd677..fd28b65 100644 --- a/diplomacy/maps/svg/pure.svg +++ b/diplomacy/maps/svg/pure.svg @@ -11,11 +11,11 @@ - + - - - + + + @@ -29,54 +29,42 @@ - - - - - - - - - - + + + + + - - - - - + + + + + + + + - - - - + + + + + + + - - - - - - - - - + + + - - - - - - - - - - - - - - + + + + + + + Pure @@ -85,36 +73,9 @@ /* text */ svg { font-size: 100% } - .titletext {text-anchor:middle; stroke-width:0.3; font-family:sans-serif; font-size:0.7em; stroke:black; fill:black;} - .provtext {text-anchor:middle; stroke-width:0.3; font-family:sans-serif; font-size:0.7em; stroke:black; fill:black;} .labeltext {stroke-width:0.1; stroke:black; fill:black;} - .unordered {fill:red; stroke:black; stroke-width:1; fill-opacity:0.90;} - .labeltext24 {text-anchor:middle; stroke-width:0.1; stroke:black; fill:black; font-family:serif,sans-serif; font-style:italic; font-size:1.4em;} - .labeltext18 {text-anchor:middle; stroke-width:0.1; stroke:black; fill:black; font-family:serif,sans-serif; font-style:italic; font-size:1.1em;} - .fulllabeltext {font-family:serif,sans-serif; font-style:italic; font-size:1.2em; fill:black; stroke:black;} .currentnotetext {font-family:serif,sans-serif; font-size:1.5em; fill:black; stroke:black;} - .currentnoterect {fill:#c5dfea;} - .currentphasetext {font-family:serif,sans-serif; font-size:2.5em; fill:black; stroke:black;} - - .labeltext24 text {cursor:default;} - - /* NB: this style is not yet used. */ - .labeltext24 text.allowed { - font-weight:bold; - fill:blue; - font-size: 1.5em; - stroke: red; - stroke-width:2; - text-decoration: underline; - background-color: green; - } - - /* map and object features */ - - .seapoly {stroke:#000000; stroke-width:1; fill:#B5DEF8} - .dashline {stroke:darkslateblue; stroke-width:3; stroke-linecap:round; stroke-dasharray:5,6;} - .impassable {fill:#353433; stroke:#000000; stroke-width:1} - .sealine {stroke:#B5DEFF; stroke-width:3;} + .currentphasetext {font-family:serif,sans-serif; fill:black; stroke:black;} /* invisible click rects fill:none does not work */ @@ -122,85 +83,50 @@ /* default region coloring, by power */ - .provinceRed {fill:url(#patternRed)} - .provinceBrown {fill:url(#patternBrown)} - .provinceGreen {fill:url(#patternGreen)} - .provinceBlack {fill:url(#patternBlack)} - .provinceBlue {fill:url(#patternBlue)} + .provinceRed {fill:url(#patternRed); stroke: black; stroke-width: 2} + .provinceBrown {fill:url(#patternBrown); stroke: black; stroke-width: 2} + .provinceGreen {fill:url(#patternGreen); stroke: black; stroke-width: 2} + .provinceBlack {fill:url(#patternBlack); stroke: black; stroke-width: 2} + .provinceBlue {fill:url(#patternBlue); stroke: black; stroke-width: 2} - .nopower {fill:antiquewhite; stroke:#000000; stroke-width:1} - .water {fill:#c5dfea; stroke:#000000; stroke-width:1} + .nopower {fill:antiquewhite; stroke:#000000; stroke-width:2} + .water {fill:#c5dfea; stroke:#000000; stroke-width:2} - .neutral {fill:lightgray; stroke:#000000; stroke-width:1} - - .austria {fill:#c48f85; stroke:#000000; stroke-width:1} - .england {fill:darkviolet; stroke:#000000; stroke-width:1} - .france {fill:royalblue; stroke:#000000; stroke-width:1} - .germany {fill:#a08a75; stroke:#000000; stroke-width:1} - .italy {fill:forestgreen; stroke:#000000; stroke-width:1} - .russia {fill:#757d91; stroke:#000000; stroke-width:1} - .turkey {fill:#b9a61c; stroke:#000000; stroke-width:1} + .austria {fill:#c48f85; stroke:#000000; stroke-width:2} + .england {fill:darkviolet; stroke:#000000; stroke-width:2} + .france {fill:royalblue; stroke:#000000; stroke-width:2} + .germany {fill:#a08a75; stroke:#000000; stroke-width:2} + .italy {fill:forestgreen; stroke:#000000; stroke-width:2} + .russia {fill:#757d91; stroke:#000000; stroke-width:2} + .turkey {fill:#b9a61c; stroke:#000000; stroke-width:2} /* unit colors, by power note that underscores are not supported */ .unitaustria {fill:red; fill-opacity:0.85} .unitengland {fill:mediumpurple; fill-opacity:0.85} - .unitfrance {fill:deepskyblue; fill-opacity:0.85} + .unitfrance {fill:deepskyblue; fill-opacity:0.85} .unitgermany {fill:dimgray; fill-opacity:0.85} - .unititaly {fill:olive; fill-opacity:0.85} - .unitrussia {fill:white; fill-opacity:1.0} - .unitturkey {fill:yellow; fill-opacity:0.85} - - /* supply center styles */ - - .scnopower {fill:black; stroke:black;} - .scaustria {fill:black; stroke:black;} - .scengland {fill:black; stroke:black;} - .scfrance {fill:black; stroke:black;} - .scgermany {fill:black; stroke:black;} - .scitaly {fill:black; stroke:black;} - .scrussia {fill:black; stroke:black;} - .scturkey {fill:black; stroke:black;} + .unititaly {fill:olive; fill-opacity:0.85} + .unitrussia {fill:white; fill-opacity:1.0} + .unitturkey {fill:yellow; fill-opacity:0.85} /* order drawing styles, stroke and fill colors should not be specified */ - .defaultorder {stroke-width:6; fill:none;} .supportorder {stroke-width:6; fill:none; stroke-dasharray:5,5;} - .convoyorder {stroke-dasharray:15,5; stroke-width:6; fill:none;} - - .shadoworder {stroke-width:10; fill:none; stroke:black;} - .shadowdash {stroke-width:10; fill:none; stroke:black; opacity:0.45;} - - .varwidthorder {fill:none;} + .convoyorder {stroke-dasharray:15,5; stroke-width:6; fill:none;} + .shadowdash {stroke-width:10; fill:none; stroke:black; opacity:0.45;} + .varwidthorder {fill:none;} .varwidthshadow {fill:none; stroke:black;} - /* Symbol private styles. Always start with "sym" to avoid name collisions! */ - - .symBuildShadow {fill:none;stroke:black;opacity:0.5;stroke-width:7;} - .symBuild {stroke:yellow;stroke-width:7;fill:none;} - .symRemove {stroke:red;stroke-width:1;fill:none;} - - .symShadow {stroke:black;fill:black;stroke-width:1;opacity:0.40;} - .symDislodgedShadow {stroke:red;fill:red;stroke-width:1;opacity:0.50;} - .symDislodgedBorder {stroke:red;stroke-width:3%;} - - .symDarkener {fill:black;opacity:0.45;fill-opacity:0.45;} - .symCenterHub {fill:black; stroke:black; opacity:0.60; stroke-width:0.5px;} - .symBorder {stroke:black;stroke-width:3%;} - .symThinBorder {stroke:black;stroke-width:0.4;} - .symSilhouette {stroke:black;fill:black;stroke-width:1;} - ]]> - + + + @@ -216,15 +142,15 @@ - + - + - + @@ -233,14 +159,15 @@ - - + + + @@ -248,19 +175,40 @@ - + - + + + + + + + + + + + + + + + + + + + + + + - - - + + + @@ -274,9 +222,9 @@ - - - + + + @@ -291,9 +239,9 @@ - - - + + + @@ -307,9 +255,9 @@ - - - + + + @@ -325,23 +273,23 @@ - + - + - + - + - + @@ -388,12 +336,12 @@ - - - - - - + + + + + + @@ -401,32 +349,32 @@ Paris + font-family="sansserif" font-size="36" + transform="translate(-123.913 110.87) translate(1263.04 589.13) translate(-32.6087 2.17391) rotate(309.832 -221.264 125.31) translate(-7.1597 -41.6087) translate(14.9305 -15.5542)" + >Paris London + font-family="sansserif" x="-287px" y="205px" + transform="translate(-119.565 -19.5652) translate(1354.35 219.565) translate(-64.1975 1.23457) rotate(75.7072 -220.85 195.892) translate(26.8838 14.493) translate(-21.3531 -2.98297) translate(0.45683 6.30804)" + >London Vienna + font-family="sansserif" x="-274px" y="308px" + transform="translate(-132.609 -197.826) translate(2.17391 6.52174) translate(1069.57 -10.8696) rotate(29.8179 -218.768 295.31) translate(10.2944 29.5644) rotate(358.562 -220.214 298.775) rotate(359.007 -220.285 298.603) rotate(357.849 -220.329 298.482) translate(0.720196 6.03851) translate(-8.46365 -1.53838) translate(-11.2897 -18.3683)" + >Vienna Berlin + font-family="sansserif" x="-313px" y="390px" + transform="translate(-93.4783 -84.7826) translate(839.13 676.087) translate(-8.69565 -2.17391) translate(0 2.17391) translate(-2.38095 -21.4286) translate(0 5.76923) translate(23 -2) translate(7 0)" + >Berlin Constantinople + font-family="sansserif" x="-333px" y="475px" + transform="translate(-60.8696 0) translate(586.957 -369.565) translate(32.6923 23.0769) rotate(335.957 -277.737 465.894) translate(17.1839 -0.756818) translate(-1.37782 7.80842) translate(0.972716 2.53974) translate(6.55609 -20.5395) rotate(358.255 -213.197 464.84) translate(-50.1197 -2.00399) translate(-10.8147 -1.39563) translate(-2.30251 0.802347) translate(2.35477 3.05259)" + >Constantinople Moscow + font-family="sansserif" x="-322px" y="551px" + transform="translate(-71.7391 -130.435) translate(6.52174 0) translate(6.52174 2.17391) translate(330.435 6.52174) translate(1.92308 0) translate(50 0) translate(-7.69231 0) rotate(285.776 -266.269 538.335) translate(1.28651 11.6267) rotate(357.124 -268.227 538.669) translate(20.3562 -7.10479)" + >Moscow Rome @@ -438,13 +386,21 @@ - + - S1901M + S1901M - + + + + + + + + + @@ -452,19 +408,19 @@ - + - + - + - + - + diff --git a/diplomacy/maps/svg/standard.svg b/diplomacy/maps/svg/standard.svg index b6450df..5eada69 100644 --- a/diplomacy/maps/svg/standard.svg +++ b/diplomacy/maps/svg/standard.svg @@ -1,10 +1,10 @@ - - - - + + + + @@ -35,403 +35,361 @@ - - - - - - - - - - + + + + + - - + + - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - - + + - - - + + - - + + - - + + - - + + - - - + + - - - + + - - - + + + + + + + + + + - - + + - - - + + - - - + + - - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - - + + - - + + - - - + + - - + + - - + + - - - + + - - - + + - - + + - - - + + - - - - - - - + + - - + + + + + + - - + + - - - + + - - - + + - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - - + + - - - + + - - + + - - + + - - - + + - - + + - - - + + - - + + - - - + + - - - + + - - - + + - - + + - - + + - - - + + - - - + + + + + + + + + + - - - + + + + + + + + + + - - - + + - - + + - - + + - - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - - + + - - - + + - - - + + - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -440,36 +398,17 @@ /* text */ svg { font-size: 100% } - .titletext {text-anchor:middle; stroke-width:0.3; font-family:sans-serif; font-size:0.7em; stroke:black; fill:black;} - .provtext {text-anchor:middle; stroke-width:0.3; font-family:sans-serif; font-size:0.7em; stroke:black; fill:black;} - .labeltext {stroke-width:0.1; stroke:black; fill:black;} - .unordered {fill:red; stroke:black; stroke-width:1; fill-opacity:0.90;} .labeltext24 {text-anchor:middle; stroke-width:0.1; stroke:black; fill:black; font-family:serif,sans-serif; font-style:italic; font-size:1.4em;} .labeltext18 {text-anchor:middle; stroke-width:0.1; stroke:black; fill:black; font-family:serif,sans-serif; font-style:italic; font-size:1.1em;} - .fulllabeltext {font-family:serif,sans-serif; font-style:italic; font-size:1.2em; fill:black; stroke:black;} .currentnotetext {font-family:serif,sans-serif; font-size:1.5em; fill:black; stroke:black;} .currentnoterect {fill:#c5dfea;} .currentphasetext {font-family:serif,sans-serif; font-size:2.5em; fill:black; stroke:black;} .labeltext24 text {cursor:default;} - /* NB: this style is not yet used. */ - .labeltext24 text.allowed { - font-weight:bold; - fill:blue; - font-size: 1.5em; - stroke: red; - stroke-width:2; - text-decoration: underline; - background-color: green; - } - /* map and object features */ - .seapoly {stroke:#000000; stroke-width:1; fill:#B5DEF8} - .dashline {stroke:darkslateblue; stroke-width:3; stroke-linecap:round; stroke-dasharray:5,6;} .impassable {fill:#353433; stroke:#000000; stroke-width:1} - .sealine {stroke:#B5DEFF; stroke-width:3;} /* invisible click rects fill:none does not work */ @@ -500,54 +439,25 @@ .unitaustria {fill:red; fill-opacity:0.85} .unitengland {fill:mediumpurple; fill-opacity:0.85} - .unitfrance {fill:deepskyblue; fill-opacity:0.85} + .unitfrance {fill:deepskyblue; fill-opacity:0.85} .unitgermany {fill:dimgray; fill-opacity:0.85} - .unititaly {fill:olive; fill-opacity:0.85} - .unitrussia {fill:white; fill-opacity:1.0} - .unitturkey {fill:yellow; fill-opacity:0.85} - - /* supply center styles */ - - .scnopower {fill:black; stroke:black;} - .scaustria {fill:black; stroke:black;} - .scengland {fill:black; stroke:black;} - .scfrance {fill:black; stroke:black;} - .scgermany {fill:black; stroke:black;} - .scitaly {fill:black; stroke:black;} - .scrussia {fill:black; stroke:black;} - .scturkey {fill:black; stroke:black;} + .unititaly {fill:olive; fill-opacity:0.85} + .unitrussia {fill:white; fill-opacity:1.0} + .unitturkey {fill:yellow; fill-opacity:0.85} /* order drawing styles, stroke and fill colors should not be specified */ - .defaultorder {stroke-width:6; fill:none;} .supportorder {stroke-width:6; fill:none; stroke-dasharray:5,5;} - .convoyorder {stroke-dasharray:15,5; stroke-width:6; fill:none;} - - .shadoworder {stroke-width:10; fill:none; stroke:black;} - .shadowdash {stroke-width:10; fill:none; stroke:black; opacity:0.45;} - - .varwidthorder {fill:none;} + .convoyorder {stroke-dasharray:15,5; stroke-width:6; fill:none;} + .shadowdash {stroke-width:10; fill:none; stroke:black; opacity:0.45;} + .varwidthorder {fill:none;} .varwidthshadow {fill:none; stroke:black;} - /* Symbol private styles. Always start with "sym" to avoid name collisions! */ - - .symBuildShadow {fill:none;stroke:black;opacity:0.5;stroke-width:7;} - .symBuild {stroke:yellow;stroke-width:7;fill:none;} - .symRemove {stroke:red;stroke-width:1;fill:none;} - - .symShadow {stroke:black;fill:black;stroke-width:1;opacity:0.40;} - .symDislodgedShadow {stroke:red;fill:red;stroke-width:1;opacity:0.50;} - .symDislodgedBorder {stroke:red;stroke-width:3%;} - - .symDarkener {fill:black;opacity:0.45;fill-opacity:0.45;} - .symCenterHub {fill:black; stroke:black; opacity:0.60; stroke-width:0.5px;} - .symBorder {stroke:black;stroke-width:3%;} - .symThinBorder {stroke:black;stroke-width:0.4;} - .symSilhouette {stroke:black;fill:black;stroke-width:1;} - ]]> + + @@ -563,15 +473,15 @@ - + - + - + @@ -580,14 +490,15 @@ - - + + + @@ -595,19 +506,40 @@ - + - + + + + + + + + + + + + + + + + + + + + + + - - - + + + @@ -621,9 +553,9 @@ - - - + + + @@ -638,9 +570,9 @@ - - - + + + @@ -654,9 +586,9 @@ - - - + + + @@ -779,7 +711,42 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/diplomacy/server/server.py b/diplomacy/server/server.py index 2281e81..e0d0dee 100644 --- a/diplomacy/server/server.py +++ b/diplomacy/server/server.py @@ -530,6 +530,9 @@ class Server(): :param server_game: server game to check :type server_game: ServerGame """ + if server_game.map.root_map != 'standard': + # Bot does not currently support other maps. + return dummy_power_names = [] if server_game.is_game_active or server_game.is_game_paused: dummy_power_names = server_game.get_dummy_unordered_power_names() diff --git a/diplomacy/utils/equilateral_triangle.py b/diplomacy/utils/equilateral_triangle.py new file mode 100644 index 0000000..d31fe79 --- /dev/null +++ b/diplomacy/utils/equilateral_triangle.py @@ -0,0 +1,173 @@ +# ============================================================================== +# Copyright (C) 2019 - Philip Paquette, Steven Bocco +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see . +# ============================================================================== +# pylint: disable=anomalous-backslash-in-string +""" Helper class to compute intersection of a line (OM) with a side of an equilateral triangle, + with O the barycenter of the equilateral triangle and M a point outside the triangle. + A + / | M + / O | + C /______| B + + A = top, B = right, C = left + O = center of triangle + M = point outside of triangle +""" +class EquilateralTriangle: + """ Helper class that represent an equilateral triangle. + Used to compute intersection of a line with a side of convoy symbol, which is an equilateral triangle. + """ + __slots__ = ('x_a', 'y_a', 'x_b', 'y_b', 'x_c', 'y_c', 'x_o', 'y_o', 'height', + 'line_ab_a', 'line_ab_b', 'line_ac_a', 'line_ac_b') + + def __init__(self, x_top, y_top, x_right, y_right, x_left, y_left): + """ Constructor """ + assert y_left == y_right > y_top + assert x_left < x_top < x_right + self.x_a = x_top + self.y_a = y_top + self.x_b = x_right + self.y_b = y_right + self.x_c = x_left + self.y_c = y_left + self.height = self.y_b - self.y_a + self.x_o = self.x_a + self.y_o = self.y_a + 2 * self.height / 3 + self.line_ab_a = (self.y_b - self.y_a) / (self.x_b - self.x_a) + self.line_ab_b = self.y_b - self.x_b * self.line_ab_a + self.line_ac_a = (self.y_c - self.y_a) / (self.x_c - self.x_a) + self.line_ac_b = self.y_c - self.x_c * self.line_ac_a + + def __line_om(self, x_m, y_m): + """ Returns the slope and the intersect of the line between O and M + :return: a, b - respectively the slope and the intercept of the line OM + """ + # pylint:disable=invalid-name + a = (y_m - self.y_o) / (x_m - self.x_o) + b = y_m - a * x_m + return a, b + + def _intersection_with_ab(self, x_m, y_m): + """ Return coordinates of intersection of line (OM) with line (AB). + :param x_m: x coordinate of M + :param y_m: y coordinate of M + :return: coordinates (x, y) of intersection, or (None, None) if either + (OM) and (AB) don't intersect, or intersection point is not in segment AB. + """ + # pylint:disable=invalid-name + a, b = self.line_ab_a, self.line_ab_b + if x_m == self.x_o: + # (OM) is a vertical line + x = x_m + else: + u, v = self.__line_om(x_m, y_m) + if a == u: + # (OM) and (AB) are parallel. No intersection. + return None, None + x = (v - b) / (a - u) + y = a * x + b + if self.x_a <= x <= self.x_b and self.y_a <= y <= self.y_b: + return x, y + return None, None + + def _intersection_with_ac(self, x_m, y_m): + """ Return coordinates of intersection of line (OM) with line (AC). + :param x_m: x coordinate of M + :param y_m: y coordinate of M + :return: coordinates (x, y) of intersection, or (None, None) if either + (OM) and (AC) don't intersect, or intersection point is not in segment AC. + """ + # pylint:disable=invalid-name + a, b = self.line_ac_a, self.line_ac_b + if x_m == self.x_o: + x = x_m + else: + u, v = self.__line_om(x_m, y_m) + if a == u: + return None, None + x = (v - b) / (a - u) + y = a * x + b + if self.x_c <= x <= self.x_a and self.y_a <= y <= self.y_c: + return x, y + return None, None + + def _intersection_with_bc(self, x_m, y_m): + """ Return coordinates of intersection of line (OM) with line (BC). + NB: (BC) is an horizontal line. + :param x_m: x coordinate of M + :param y_m: y coordinate of M + :return: coordinates (x, y) of intersection, or (None, None) if either + (OM) and (BC) don't intersect, or intersection point is not in segment BC. + """ + # pylint:disable=invalid-name + y = self.y_c + if x_m == self.x_o: + x = x_m + else: + a, b = self.__line_om(x_m, y_m) + if a == 0: + return None, None + x = (y - b) / a + if self.x_c <= x <= self.x_a: + return x, y + return None, None + + def intersection(self, x_m, y_m): + """ Return coordinates of the intersection of (OM) with equilateral triangle, + with M the point with coordinates (x_m, y_m). Only the intersection with + the side of triangle near M is considered. + :param x_m: x coordinate of M + :param y_m: y coordinate of M + :return: a couple (x, y) of floating values. + """ + # pylint:disable=invalid-name + if self.x_o == x_m and self.y_o == y_m: + return x_m, y_m + + if self.x_o == x_m: + if y_m < self.y_o: + return x_m, self.y_a + # Otherwise, vertical line intersects BC + return x_m, self.y_c + + if self.y_o == y_m: + if x_m < self.x_o: + # horizontal line intersects AC + a, b = self.line_ac_a, self.line_ac_b + else: + # horizontal line intersects AB + a, b = self.line_ab_a, self.line_ab_b + x = (y_m - b) / a + return x, y_m + + # Otherwise, get nearest point in intersections with AB, AC, BC + p1_x, p1_y = self._intersection_with_ab(x_m, y_m) + p2_x, p2_y = self._intersection_with_ac(x_m, y_m) + p3_x, p3_y = self._intersection_with_bc(x_m, y_m) + distances = [] + if p1_x is not None: + distance_1 = ((p1_x - x_m) ** 2 + (p1_y - y_m) ** 2) ** 0.5 + distances.append((distance_1, p1_x, p1_y)) + if p2_x is not None: + distance_2 = ((p2_x - x_m) ** 2 + (p2_y - y_m) ** 2) ** 0.5 + distances.append((distance_2, p2_x, p2_y)) + if p3_x is not None: + distance_3 = ((p3_x - x_m) ** 2 + (p3_y - y_m) ** 2) ** 0.5 + distances.append((distance_3, p3_x, p3_y)) + + assert distances + distances.sort() + return distances[0][1:] diff --git a/diplomacy/web/convert_svg_maps_to_react.sh b/diplomacy/web/convert_svg_maps_to_react.sh new file mode 100755 index 0000000..9d6c1d4 --- /dev/null +++ b/diplomacy/web/convert_svg_maps_to_react.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +python svg_to_react.py --input src/diplomacy/maps/svg/standard.svg --output src/gui/maps/standard/ --name SvgStandard +python svg_to_react.py --input src/diplomacy/maps/svg/ancmed.svg --output src/gui/maps/ancmed/ --name SvgAncMed +python svg_to_react.py --input src/diplomacy/maps/svg/modern.svg --output src/gui/maps/modern/ --name SvgModern +python svg_to_react.py --input src/diplomacy/maps/svg/pure.svg --output src/gui/maps/pure/ --name SvgPure diff --git a/diplomacy/web/src/gui/components/fancyBox.js b/diplomacy/web/src/gui/components/fancyBox.js index 1e8c07c..e3ceac8 100644 --- a/diplomacy/web/src/gui/components/fancyBox.js +++ b/diplomacy/web/src/gui/components/fancyBox.js @@ -31,8 +31,8 @@ export class FancyBox extends React.Component {
-
{this.props.children}
+
+ {this.props.children}
); diff --git a/diplomacy/web/src/gui/forms/join_form.jsx b/diplomacy/web/src/gui/forms/join_form.jsx index e7f8bb2..611c7d1 100644 --- a/diplomacy/web/src/gui/forms/join_form.jsx +++ b/diplomacy/web/src/gui/forms/join_form.jsx @@ -16,7 +16,6 @@ // ============================================================================== import React from 'react'; import {Forms} from "../components/forms"; -import {STRINGS} from "../../diplomacy/utils/strings"; import PropTypes from "prop-types"; export class JoinForm extends React.Component { @@ -53,7 +52,7 @@ export class JoinForm extends React.Component { {Forms.createLabel(this.getPowerNameID(), 'Power:')} {this.props.password_required ? ( @@ -75,6 +74,7 @@ JoinForm.propTypes = { game_id: PropTypes.string.isRequired, password_required: PropTypes.bool.isRequired, powers: PropTypes.arrayOf(PropTypes.string), + availablePowers: PropTypes.arrayOf(PropTypes.string), onChange: PropTypes.func, onSubmit: PropTypes.func }; diff --git a/diplomacy/web/src/gui/maps/ancmed/SvgAncMed.css b/diplomacy/web/src/gui/maps/ancmed/SvgAncMed.css new file mode 100644 index 0000000..0a39460 --- /dev/null +++ b/diplomacy/web/src/gui/maps/ancmed/SvgAncMed.css @@ -0,0 +1,19 @@ +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/ +/* text */ .SvgAncMed .smalllabeltext {text-anchor:middle;stroke-width:0.1;stroke:black;fill:black;font-family:serif;font-style:italic;font-size:14px;} .SvgAncMed .currentnoterect {fill:#c5dfea; stroke-width:0; stroke:black;} .SvgAncMed .currentnotetext {font-family:serif,sans-serif; font-size:12px; fill:black; stroke:black;} .SvgAncMed .currentphasetext {font-family:serif,sans-serif; font-size:18px; fill:black; stroke:black;} /* style class for invisible regions */ .SvgAncMed .invisibleContent {stroke:white;fill:white;fill-opacity:0.0;opacity:0.0} /* default region coloring, by power (nopower if region not owned) */ .SvgAncMed .provinceRed {fill:url(#patternRed); stroke: black; stroke-width:2} .SvgAncMed .provinceBrown {fill:url(#patternBrown); stroke: black; stroke-width:2} .SvgAncMed .provinceGreen {fill:url(#patternGreen); stroke: black; stroke-width:2} .SvgAncMed .provinceBlack {fill:url(#patternBlack); stroke: black; stroke-width:2} .SvgAncMed .provinceBlue {fill:url(#patternBlue); stroke: black; stroke-width:2} .SvgAncMed .nopower {fill:none;stroke:black;stroke-width:2} .SvgAncMed .water {fill:lightblue;stroke:black;stroke-width:2} .SvgAncMed .rome {fill:#c48f85;stroke:black;stroke-width:2} .SvgAncMed .carthage {fill:royalblue;stroke:black;stroke-width:2} .SvgAncMed .persia {fill:gray;stroke:black;stroke-width:2} .SvgAncMed .greece {fill:forestgreen;stroke:black;stroke-width:2} .SvgAncMed .egypt {fill:#b9a61c;stroke:black;stroke-width:2} /* unit colors fills, by power (units always have a power) */ .SvgAncMed .unitrome {fill:red;} .SvgAncMed .unitcarthage {fill:deepskyblue;} .SvgAncMed .unitpersia {fill:white;} .SvgAncMed .unitgreece {fill:#8db600;} .SvgAncMed .unitegypt {fill:yellow;} /* map accents */ .SvgAncMed .thickdash {fill:none;stroke:black;stroke-width:6;stroke-dasharray:2,1.5;} /* order drawing styles, stroke and fill colors should not be specified */ .SvgAncMed .supportorder {stroke-width:3; fill:none; stroke-dasharray:5,5;} .SvgAncMed .convoyorder {stroke-dasharray:15,5; stroke-width:3; fill:none;} .SvgAncMed .shadowdash {stroke-width:5; fill:none; stroke:black; opacity:0.45;} .SvgAncMed .varwidthorder {fill:none;} .SvgAncMed .varwidthshadow {fill:none; stroke:black;} .SvgAncMed .style1 {fill:none;stroke:#17A1F8;stroke-width:5;} .SvgAncMed .style2 {fill:none;stroke:black;stroke-width:2} \ No newline at end of file diff --git a/diplomacy/web/src/gui/maps/ancmed/SvgAncMed.js b/diplomacy/web/src/gui/maps/ancmed/SvgAncMed.js new file mode 100644 index 0000000..d726a67 --- /dev/null +++ b/diplomacy/web/src/gui/maps/ancmed/SvgAncMed.js @@ -0,0 +1,20 @@ +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/ +/** Generated with parameters: Namespace(input='src/diplomacy/maps/svg/ancmed.svg', name='SvgAncMed', output='src/gui/maps/ancmed/') **/ + import React from 'react'; import PropTypes from 'prop-types'; import './SvgAncMed.css'; import {Coordinates, SymbolSizes, Colors} from "./SvgAncMedMetadata"; import {getClickedID, parseLocation, setInfluence} from "../common/common"; import {Game} from "../../../diplomacy/engine/game"; import {MapData} from "../../utils/map_data"; import {UTILS} from "../../../diplomacy/utils/utils"; import {Diplog} from "../../../diplomacy/utils/diplog"; import {extendOrderBuilding} from "../../utils/order_building"; import {Unit} from "../common/unit"; import {Hold} from "../common/hold"; import {Move} from "../common/move"; import {SupportMove} from "../common/supportMove"; import {SupportHold} from "../common/supportHold"; import {Convoy} from "../common/convoy"; import {Build} from "../common/build"; import {Disband} from "../common/disband"; export class SvgAncMed extends React.Component { constructor(props) { super(props); this.onClick = this.onClick.bind(this); this.onHover = this.onHover.bind(this); } onClick(event) { if (this.props.orderBuilding) return this.handleClickedID(getClickedID(event)); } onHover(event) { return this.handleHoverID(getClickedID(event)); } handleClickedID(id) { const orderBuilding = this.props.orderBuilding; if (!orderBuilding.builder) return this.props.onError('No orderable locations.'); const province = this.props.mapData.getProvince(id); if (!province) throw new Error(`Cannot find a province named ${id}`); const stepLength = orderBuilding.builder.steps.length; if (orderBuilding.path.length >= stepLength) throw new Error(`Order building: current steps count (${orderBuilding.path.length}) should be less than expected steps count (${stepLength}) (${orderBuilding.path.join(', ')}).`); const lengthAfterClick = orderBuilding.path.length + 1; let validLocations = []; const testedPath = [orderBuilding.type].concat(orderBuilding.path); const value = UTILS.javascript.getTreeValue(this.props.game.ordersTree, testedPath); if (value !== null) { const checker = orderBuilding.builder.steps[lengthAfterClick - 1]; try { const possibleLocations = checker(province, orderBuilding.power); for (let possibleLocation of possibleLocations) { possibleLocation = possibleLocation.toUpperCase(); if (value.includes(possibleLocation)) validLocations.push(possibleLocation); } } catch (error) { return this.props.onError(error); } } if (!validLocations.length) return this.props.onError('Disallowed.'); if (validLocations.length > 1 && orderBuilding.type === 'S' && orderBuilding.path.length >= 2) { /* We are building a support order and we have a multiple choice for a location. */ /* Let's check if next location to choose is a coast. To have a coast: */ /* - all possible locations must start with same 3 characters. */ /* - we expect at least province name in possible locations (e.g. 'SPA' for 'SPA/NC'). */ /* If we have a coast, we will remove province name from possible locations. */ let isACoast = true; let validLocationsNoProvinceName = []; for (let i = 0; i < validLocations.length; ++i) { let location = validLocations[i]; if (i > 0) { /* Compare 3 first letters with previous location. */ if (validLocations[i - 1].substring(0, 3).toUpperCase() !== validLocations[i].substring(0, 3).toUpperCase()) { /* No same prefix with previous location. We does not have a coast. */ isACoast = false; break; } } if (location.length !== 3) validLocationsNoProvinceName.push(location); } if (validLocations.length === validLocationsNoProvinceName.length) { /* We have not found province name. */ isACoast = false; } if (isACoast) { /* We want to choose location in a coastal province. Let's remove province name. */ validLocations = validLocationsNoProvinceName; } } if (validLocations.length > 1) { if (this.props.onSelectLocation) { return this.props.onSelectLocation(validLocations, orderBuilding.power, orderBuilding.type, orderBuilding.path); } else { Diplog.warn(`Forced to select first valid location.`); validLocations = [validLocations[0]]; } } let orderBuildingType = orderBuilding.type; if (lengthAfterClick === stepLength && orderBuildingType === 'M') { const moveOrderPath = ['M'].concat(orderBuilding.path, validLocations[0]); const moveTypes = UTILS.javascript.getTreeValue(this.props.game.ordersTree, moveOrderPath); if (moveTypes !== null) { if (moveTypes.length === 2 && this.props.onSelectVia) { /* This move can be done either regularly or VIA a fleet. Let user choose. */ return this.props.onSelectVia(validLocations[0], orderBuilding.power, orderBuilding.path); } else { orderBuildingType = moveTypes[0]; } } } extendOrderBuilding( orderBuilding.power, orderBuildingType, orderBuilding.path, validLocations[0], this.props.onOrderBuilding, this.props.onOrderBuilt, this.props.onError ); } handleHoverID(id) { if (this.props.onHover) { const province = this.props.mapData.getProvince(id); if (province) { this.props.onHover(province.name, this.getRelatedOrders(province.name)); } } } getRelatedOrders(name) { const orders = []; if (this.props.orders) { for (let powerOrders of Object.values(this.props.orders)) { if (powerOrders) { for (let order of powerOrders) { const pieces = order.split(/ +/); if (pieces[1].slice(0, 3) === name.toUpperCase().slice(0, 3)) orders.push(order); } } } } return orders; } getNeighbors(extraLocation) { const selectedPath = [this.props.orderBuilding.type].concat(this.props.orderBuilding.path); if (extraLocation) selectedPath.push(extraLocation); const possibleNeighbors = UTILS.javascript.getTreeValue(this.props.game.ordersTree, selectedPath); const neighbors = possibleNeighbors ? possibleNeighbors.map(neighbor => parseLocation(neighbor)) : []; return neighbors.length ? neighbors: null; } render() { const classes = {"_adr":"water","_aeg":"water","_ale":"nopower","_ant":"nopower","_apu":"nopower","_ara":"nopower","_arm":"nopower","_ath":"nopower","_aus":"water","_bal":"nopower","_bay":"nopower","_ber":"water","_bit":"nopower","_bla":"water","_byz":"nopower","_cap":"nopower","_car":"nopower","_che":"nopower","_cil":"water","_cir":"nopower","_cor":"nopower","_cre":"nopower","_cyp":"nopower","_cyr":"nopower","_dac":"nopower","_dal":"nopower","_dam":"nopower","_egy":"water","_epi":"nopower","_etr":"nopower","_gal":"nopower","_gau":"nopower","_gop":"water","_gos":"water","_got":"water","_ibe":"water","_ill":"nopower","_ion":"water","_isa":"nopower","_jer":"nopower","_lep":"nopower","_lib":"water","_lig":"water","_lus":"nopower","_mac":"nopower","_mar":"nopower","_mas":"nopower","_mau":"nopower","_mem":"nopower","_mes":"water","_mil":"nopower","_min":"water","_nab":"nopower","_nea":"nopower","_num":"nopower","_pet":"nopower","_pha":"nopower","_pun":"water","_rav":"nopower","_ree":"water","_rha":"nopower","_rom":"nopower","_sag":"nopower","_sah":"nopower","_sad":"nopower","_sam":"nopower","_sic":"nopower","_sid":"nopower","_sin":"nopower","_sip":"nopower","_spa":"nopower","_syr":"water","_tar":"nopower","_tha":"nopower","_thb":"nopower","_tye":"nopower","_tyn":"water","_ven":"nopower","_vin":"nopower","water":"water","BriefLabelLayer":"smalllabeltext","CurrentNote":"currentnotetext","CurrentNote2":"currentnotetext","CurrentPhase":"currentphasetext","MouseLayer":"invisibleContent"}; const game = this.props.game; const mapData = this.props.mapData; const orders = this.props.orders; /* Current phase. */ const current_phase = (game.phase[0] === '?' || game.phase === 'COMPLETED') ? 'FINAL' : game.phase; /* Notes. */ const nb_centers = []; for (let power of Object.values(game.powers)) { if (!power.isEliminated()) nb_centers.push([power.name.substr(0, 3), power.centers.length]); } /* Sort nb_centers by descending number of centers. */ nb_centers.sort((a, b) => { return -(a[1] - b[1]) || a[0].localeCompare(b[0]); }); const nb_centers_per_power = nb_centers.map((couple) => (couple[0] + ': ' + couple[1])).join(' '); const note = game.note; /* Adding units, influence and orders. */ const renderedUnits = []; const renderedDislodgedUnits = []; const renderedOrders = []; const renderedOrders2 = []; const renderedHighestOrders = []; for (let power of Object.values(game.powers)) if (!power.isEliminated()) { for (let unit of power.units) { renderedUnits.push( ); } for (let unit of Object.keys(power.retreats)) { renderedDislodgedUnits.push( ); } for (let center of power.centers) { setInfluence(classes, mapData, center, power.name); } for (let loc of power.influence) { if (!mapData.supplyCenters.has(loc)) setInfluence(classes, mapData, loc, power.name); } if (orders) { const powerOrders = (orders && orders.hasOwnProperty(power.name) && orders[power.name]) || []; for (let order of powerOrders) { const tokens = order.split(/ +/); if (!tokens || tokens.length < 3) continue; const unit_loc = tokens[1]; if (tokens[2] === 'H') { renderedOrders.push( ); } else if (tokens[2] === '-') { const destLoc = tokens[tokens.length - (tokens[tokens.length - 1] === 'VIA' ? 2 : 1)]; renderedOrders.push( ); } else if (tokens[2] === 'S') { const destLoc = tokens[tokens.length - 1]; if (tokens.includes('-')) { const srcLoc = tokens[4]; renderedOrders2.push( ); } else { renderedOrders2.push( ); } } else if (tokens[2] === 'C') { const srcLoc = tokens[4]; const destLoc = tokens[tokens.length - 1]; if ((srcLoc !== destLoc) && (tokens.includes('-'))) { renderedOrders2.push( ); } } else if (tokens[2] === 'B') { renderedHighestOrders.push( ); } else if (tokens[2] === 'D') { renderedHighestOrders.push( ); } else if (tokens[2] === 'R') { const destLoc = tokens[3]; renderedOrders.push( ); } else { throw new Error(`Unknown error to render (${order}).`); } } } } if (this.props.orderBuilding && this.props.orderBuilding.path.length) { const clicked = parseLocation(this.props.orderBuilding.path[0]); const province = this.props.mapData.getProvince(clicked); if (!province) throw new Error(('Unknown clicked province ' + clicked)); const clickedID = province.getID(classes); if (!clicked) throw new Error(`Unknown path (${clickedID}) for province (${clicked}).`); classes[clickedID] = 'provinceRed'; const neighbors = this.getNeighbors(); if (neighbors) { for (let neighbor of neighbors) { const neighborProvince = this.props.mapData.getProvince(neighbor); if (!neighborProvince) throw new Error('Unknown neighbor province ' + neighbor); const neighborID = neighborProvince.getID(classes); if (!neighborID) throw new Error(`Unknown neoghbor path (${neighborID}) for province (${neighbor}).`); classes[neighborID] = neighborProvince.isWater() ? 'provinceBlue' : 'provinceGreen'; } } } if (this.props.showAbbreviations === false) { classes['BriefLabelLayer'] = 'visibilityHidden'; } return ( Ancient Med Map {renderedOrders2} {renderedOrders} {renderedUnits} {renderedDislodgedUnits} {renderedHighestOrders} adr aeg ale ant apu ara arm ath aus bal bay ber bit bla byz cap car che cil cir cor cre cyp cyr dac dal dam egy epi etr gal gau gop gos got ibe ill ion isa jer lep lib lig lus mac mar mas mau mem mes mil min nab nea num pet pha pun rav ree rha rom sag sah sad sam sic sid sin sip spa syr tar tha thb tye tyn ven vin {nb_centers_per_power ? nb_centers_per_power : ''} {note ? note : ''} {current_phase} ); } } SvgAncMed.propTypes = { game: PropTypes.instanceOf(Game).isRequired, mapData: PropTypes.instanceOf(MapData).isRequired, orders: PropTypes.object, onHover: PropTypes.func, onError: PropTypes.func.isRequired, onSelectLocation: PropTypes.func, onSelectVia: PropTypes.func, onOrderBuilding: PropTypes.func, onOrderBuilt: PropTypes.func, orderBuilding: PropTypes.object, showAbbreviations: PropTypes.bool }; // eslint-disable-line semi \ No newline at end of file diff --git a/diplomacy/web/src/gui/maps/ancmed/SvgAncMedMetadata.js b/diplomacy/web/src/gui/maps/ancmed/SvgAncMedMetadata.js new file mode 100644 index 0000000..c01e4dd --- /dev/null +++ b/diplomacy/web/src/gui/maps/ancmed/SvgAncMedMetadata.js @@ -0,0 +1,21 @@ +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/ +export const Coordinates = {"ADR":{"unit":["472.5","201.0"],"disl":["479.5","194.0"]},"AEG":{"unit":["657.5","335.0"],"disl":["664.5","328.0"]},"ALE":{"unit":["705.5","504.0"],"disl":["712.5","497.0"]},"ANT":{"unit":["911.5","304.0"],"disl":["918.5","297.0"]},"APU":{"unit":["443.5","222.0"],"disl":["450.5","215.0"]},"ARA":{"unit":["971.5","416.0"],"disl":["978.5","409.0"]},"ARM":{"unit":["977.5","214.0"],"disl":["984.5","207.0"]},"ATH":{"unit":["581.5","300.0"],"disl":["588.5","293.0"]},"AUS":{"unit":["485.5","382.0"],"disl":["492.5","375.0"]},"BAL":{"unit":["153.5","240.0"],"disl":["160.5","233.0"]},"BAY":{"unit":["682.5","617.0"],"disl":["689.5","610.0"]},"BER":{"unit":["205.5","315.0"],"disl":["212.5","308.0"]},"BIT":{"unit":["759.5","209.0"],"disl":["766.5","202.0"]},"BLA":{"unit":["770.5","143.0"],"disl":["777.5","136.0"]},"BYZ":{"unit":["682.5","242.0"],"disl":["689.5","235.0"]},"CAP":{"unit":["865.5","269.0"],"disl":["872.5","262.0"]},"CAR":{"unit":["303.5","372.0"],"disl":["310.5","365.0"]},"CHE":{"unit":["881.5","59.0"],"disl":["888.5","52.0"]},"CIL":{"unit":["785.5","353.0"],"disl":["792.5","346.0"]},"CIR":{"unit":["236.5","442.0"],"disl":["243.5","435.0"]},"COR":{"unit":["307.5","215.0"],"disl":["314.5","208.0"]},"CRE":{"unit":["650.5","382.0"],"disl":["657.5","375.0"]},"CYP":{"unit":["816.5","374.0"],"disl":["823.5","367.0"]},"CYR":{"unit":["625.5","508.0"],"disl":["632.5","501.0"]},"DAC":{"unit":["621.5","138.0"],"disl":["628.5","131.0"]},"DAL":{"unit":["502.5","178.0"],"disl":["509.5","171.0"]},"DAM":{"unit":["973.5","308.0"],"disl":["980.5","301.0"]},"EGY":{"unit":["728.5","441.0"],"disl":["735.5","434.0"]},"EPI":{"unit":["544.5","267.0"],"disl":["551.5","260.0"]},"ETR":{"unit":["313.5","129.0"],"disl":["320.5","122.0"]},"GAL":{"unit":["774.5","264.0"],"disl":["781.5","257.0"]},"GAU":{"unit":["197.5","83.0"],"disl":["204.5","76.0"]},"GOP":{"unit":["843.5","446.0"],"disl":["850.5","439.0"]},"GOS":{"unit":["509.5","498.0"],"disl":["516.5","491.0"]},"GOT":{"unit":["475.5","428.0"],"disl":["482.5","421.0"]},"IBE":{"unit":["82.5","356.0"],"disl":["89.5","349.0"]},"ILL":{"unit":["536.5","157.0"],"disl":["543.5","150.0"]},"ION":{"unit":["508.5","306.0"],"disl":["515.5","299.0"]},"ISA":{"unit":["806.5","313.0"],"disl":["813.5","306.0"]},"JER":{"unit":["899.5","451.0"],"disl":["906.5","444.0"]},"LEP":{"unit":["468.5","533.0"],"disl":["475.5","526.0"]},"LIB":{"unit":["544.5","428.0"],"disl":["551.5","421.0"]},"LIG":{"unit":["256.5","215.0"],"disl":["263.5","208.0"]},"LUS":{"unit":["18.5","227.0"],"disl":["25.5","220.0"]},"MAC":{"unit":["611.5","231.0"],"disl":["618.5","224.0"]},"MAR":{"unit":["601.5","561.0"],"disl":["608.5","554.0"]},"MAS":{"unit":["253.5","127.0"],"disl":["260.5","120.0"]},"MAU":{"unit":["112.5","410.0"],"disl":["119.5","403.0"]},"MEM":{"unit":["783.5","554.0"],"disl":["790.5","547.0"]},"MES":{"unit":["535.5","378.0"],"disl":["542.5","371.0"]},"MIL":{"unit":["730.5","313.0"],"disl":["737.5","306.0"]},"MIN":{"unit":["709.5","370.0"],"disl":["716.5","363.0"]},"NAB":{"unit":["976.5","528.0"],"disl":["983.5","521.0"]},"NEA":{"unit":["457.5","274.0"],"disl":["464.5","267.0"]},"NUM":{"unit":["343.5","471.0"],"disl":["350.5","464.0"]},"PET":{"unit":["917.5","508.0"],"disl":["924.5","501.0"]},"PHA":{"unit":["400.5","553.0"],"disl":["407.5","546.0"]},"PUN":{"unit":["358.5","338.0"],"disl":["365.5","331.0"]},"RAV":{"unit":["391.5","169.0"],"disl":["398.5","162.0"]},"REE":{"unit":["950.5","594.0"],"disl":["957.5","587.0"]},"RHA":{"unit":["339.5","59.0"],"disl":["346.5","52.0"]},"ROM":{"unit":["368.5","188.0"],"disl":["375.5","181.0"]},"SAD":{"unit":["304.5","264.0"],"disl":["311.5","257.0"]},"SAG":{"unit":["58.5","302.0"],"disl":["65.5","295.0"]},"SAH":{"unit":["269.5","588.0"],"disl":["276.5","581.0"]},"SAM":{"unit":["517.5","56.0"],"disl":["524.5","49.0"]},"SIC":{"unit":["421.5","333.0"],"disl":["428.5","326.0"]},"SID":{"unit":["919.5","355.0"],"disl":["926.5","348.0"]},"SIN":{"unit":["871.5","490.0"],"disl":["878.5","483.0"]},"SIP":{"unit":["865.5","196.0"],"disl":["872.5","189.0"]},"SPA":{"unit":["583.5","322.0"],"disl":["590.5","315.0"]},"SYR":{"unit":["864.5","402.0"],"disl":["871.5","395.0"]},"TAR":{"unit":["128.5","221.0"],"disl":["135.5","214.0"]},"THA":{"unit":["307.5","419.0"],"disl":["314.5","412.0"]},"THB":{"unit":["879.5","591.0"],"disl":["886.5","584.0"]},"TYE":{"unit":["914.5","383.0"],"disl":["921.5","376.0"]},"TYN":{"unit":["395.5","268.0"],"disl":["402.5","261.0"]},"VEN":{"unit":["346.5","100.0"],"disl":["353.5","93.0"]},"VIN":{"unit":["410.5","71.0"],"disl":["417.5","64.0"]}}; +export const SymbolSizes = {"Fleet":{"width":20.0,"height":20.0},"Army":{"width":20.0,"height":20.0},"Wing":{"width":20.0,"height":20.0},"DislodgedFleet":{"width":20.0,"height":20.0},"DislodgedArmy":{"width":20.0,"height":20.0},"DislodgedWing":{"width":20.0,"height":20.0},"FailedOrder":{"width":20.0,"height":20.0},"SupplyCenter":{"width":10.0,"height":10.0},"BuildUnit":{"width":30.0,"height":30.0},"RemoveUnit":{"width":30.0,"height":30.0},"WaivedBuild":{"width":25.0,"height":25.0},"HoldUnit":{"width":40.0,"height":40.0},"SupportHoldUnit":{"width":46.0,"height":46.0},"ConvoyTriangle":{"width":50.0,"height":43.2},"Stroke":{"width":3.0,"height":5.0}}; +export const Colors = {"ROME":"#c48f85","CARTHAGE":"royalblue","PERSIA":"gray","GREECE":"forestgreen","EGYPT":"#b9a61c"}; diff --git a/diplomacy/web/src/gui/maps/common/build.js b/diplomacy/web/src/gui/maps/common/build.js new file mode 100644 index 0000000..765dbad --- /dev/null +++ b/diplomacy/web/src/gui/maps/common/build.js @@ -0,0 +1,57 @@ +// ============================================================================== +// Copyright (C) 2019 - Philip Paquette, Steven Bocco +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License along +// with this program. If not, see . +// ============================================================================== +import React from "react"; +import {ARMY, centerSymbolAroundUnit, FLEET} from "./common"; +import PropTypes from "prop-types"; + +export class Build extends React.Component { + render() { + const Coordinates = this.props.coordinates; + const SymbolSizes = this.props.symbolSizes; + const loc = this.props.loc; + const unit_type = this.props.unitType; + const build_symbol = 'BuildUnit'; + const loc_x = Coordinates[loc].unit[0]; + const loc_y = Coordinates[loc].unit[1]; + const [build_loc_x, build_loc_y] = centerSymbolAroundUnit(Coordinates, SymbolSizes, loc, false, build_symbol); + + const symbol = unit_type === 'A' ? ARMY : FLEET; + return ( + + + + + ); + } +} + +Build.propTypes = { + unitType: PropTypes.string.isRequired, + loc: PropTypes.string.isRequired, + powerName: PropTypes.string.isRequired, + coordinates: PropTypes.object.isRequired, + symbolSizes: PropTypes.object.isRequired +}; diff --git a/diplomacy/web/src/gui/maps/common/common.js b/diplomacy/web/src/gui/maps/common/common.js new file mode 100644 index 0000000..750f786 --- /dev/null +++ b/diplomacy/web/src/gui/maps/common/common.js @@ -0,0 +1,83 @@ +// ============================================================================== +// Copyright (C) 2019 - Philip Paquette, Steven Bocco +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License along +// with this program. If not, see . +// ============================================================================== + +export const ARMY = 'Army'; +export const FLEET = 'Fleet'; + +export function offset(floatString, offset) { + return "" + (parseFloat(floatString) + offset); +} + +export function setInfluence(classes, mapData, loc, power_name) { + const province = mapData.getProvince(loc); + if (!province) + throw new Error(`Unable to find province ${loc}`); + if (!['LAND', 'COAST'].includes(province.type)) + return; + const id = province.getID(classes); + if (!id) + throw new Error(`Unable to find SVG path for loc ${id}`); + classes[id] = power_name ? power_name.toLowerCase() : 'nopower'; +} + +export function getClickedID(event) { + let node = event.target; + if (!node.id && node.parentNode.id && node.parentNode.tagName === 'g') + node = node.parentNode; + let id = node.id; + return id ? id.substr(0, 3) : null; +} + +export function parseLocation(txt) { + if (txt.length > 2 && txt[1] === ' ' && ['A', 'F'].includes(txt[0])) + return txt.substr(2); + return txt; +} + +export function centerSymbolAroundUnit(coordinates, symbolSizes, loc, isDislodged, symbol) { + const key = isDislodged ? 'disl' : 'unit'; + const unitKey = ARMY; + const [unit_x, unit_y] = coordinates[loc][key]; + const unit_height = symbolSizes[unitKey].height; + const unit_width = symbolSizes[unitKey].width; + const symbol_height = symbolSizes[symbol].height; + const symbol_width = symbolSizes[symbol].width; + return [ + `${(parseFloat(unit_x) + parseFloat(unit_width) / 2 - parseFloat(symbol_width) / 2)}`, + `${(parseFloat(unit_y) + parseFloat(unit_height) / 2 - parseFloat(symbol_height) / 2)}` + ]; +} + +export function getUnitCenter(coordinates, symbolSizes, loc, isDislodged) { + const key = isDislodged ? 'disl' : 'unit'; + const unitKey = ARMY; + const [unit_x, unit_y] = coordinates[loc][key]; + const unit_height = symbolSizes[unitKey].height; + const unit_width = symbolSizes[unitKey].width; + return [ + `${parseFloat(unit_x) + parseFloat(unit_width) / 2}`, + `${parseFloat(unit_y) + parseFloat(unit_height) / 2}` + ]; +} + +export function plainStrokeWidth(symbolSizes) { + return parseFloat(symbolSizes.Stroke.height); +} + +export function coloredStrokeWidth(symbolSizes) { + return parseFloat(symbolSizes.Stroke.width); +} diff --git a/diplomacy/web/src/gui/maps/common/convoy.js b/diplomacy/web/src/gui/maps/common/convoy.js new file mode 100644 index 0000000..c966161 --- /dev/null +++ b/diplomacy/web/src/gui/maps/common/convoy.js @@ -0,0 +1,103 @@ +// ============================================================================== +// Copyright (C) 2019 - Philip Paquette, Steven Bocco +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License along +// with this program. If not, see . +// ============================================================================== +import React from "react"; +import {ARMY, centerSymbolAroundUnit, coloredStrokeWidth, getUnitCenter} from "./common"; +import {EquilateralTriangle} from "./equilateralTriangle"; +import PropTypes from "prop-types"; + +export class Convoy extends React.Component { + render() { + const Coordinates = this.props.coordinates; + const SymbolSizes = this.props.symbolSizes; + const Colors = this.props.colors; + const loc = this.props.loc; + const src_loc = this.props.srcLoc; + const dest_loc = this.props.dstLoc; + + const symbol = 'ConvoyTriangle'; + let [symbol_loc_x, symbol_loc_y] = centerSymbolAroundUnit(Coordinates, SymbolSizes, src_loc, false, symbol); + const symbol_height = parseFloat(SymbolSizes[symbol].height); + const symbol_width = parseFloat(SymbolSizes[symbol].width); + const triangle = new EquilateralTriangle( + parseFloat(symbol_loc_x) + symbol_width / 2, + parseFloat(symbol_loc_y), + parseFloat(symbol_loc_x) + symbol_width, + parseFloat(symbol_loc_y) + symbol_height, + parseFloat(symbol_loc_x), + parseFloat(symbol_loc_y) + symbol_height + ); + symbol_loc_y = '' + (parseFloat(symbol_loc_y) - symbol_height / 6); + const [loc_x, loc_y] = getUnitCenter(Coordinates, SymbolSizes, loc, false); + const [src_loc_x, src_loc_y] = getUnitCenter(Coordinates, SymbolSizes, src_loc, false); + let [dest_loc_x, dest_loc_y] = getUnitCenter(Coordinates, SymbolSizes, dest_loc, false); + + const [src_loc_x_1, src_loc_y_1] = triangle.intersection(loc_x, loc_y); + const [src_loc_x_2, src_loc_y_2] = triangle.intersection(dest_loc_x, dest_loc_y); + + const dest_delta_x = dest_loc_x - src_loc_x; + const dest_delta_y = dest_loc_y - src_loc_y; + const dest_vector_length = Math.sqrt(dest_delta_x * dest_delta_x + dest_delta_y * dest_delta_y); + const delta_dec = parseFloat(SymbolSizes[ARMY].width) / 2 + 2 * coloredStrokeWidth(SymbolSizes); + dest_loc_x = '' + Math.round((parseFloat(src_loc_x) + (dest_vector_length - delta_dec) / dest_vector_length * dest_delta_x) * 100.) / 100.; + dest_loc_y = '' + Math.round((parseFloat(src_loc_y) + (dest_vector_length - delta_dec) / dest_vector_length * dest_delta_y) * 100.) / 100.; + + return ( + + + + + + + + ); + } +} + +Convoy.propTypes = { + loc: PropTypes.string.isRequired, + srcLoc: PropTypes.string.isRequired, + dstLoc: PropTypes.string.isRequired, + powerName: PropTypes.string.isRequired, + coordinates: PropTypes.object.isRequired, + symbolSizes: PropTypes.object.isRequired, + colors: PropTypes.object.isRequired +}; diff --git a/diplomacy/web/src/gui/maps/common/disband.js b/diplomacy/web/src/gui/maps/common/disband.js new file mode 100644 index 0000000..97d5c04 --- /dev/null +++ b/diplomacy/web/src/gui/maps/common/disband.js @@ -0,0 +1,47 @@ +// ============================================================================== +// Copyright (C) 2019 - Philip Paquette, Steven Bocco +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License along +// with this program. If not, see . +// ============================================================================== +import React from "react"; +import {centerSymbolAroundUnit} from "./common"; +import PropTypes from "prop-types"; + +export class Disband extends React.Component { + render() { + const Coordinates = this.props.coordinates; + const SymbolSizes = this.props.symbolSizes; + const loc = this.props.loc; + const phaseType = this.props.phaseType; + const symbol = 'RemoveUnit'; + const [loc_x, loc_y] = centerSymbolAroundUnit(Coordinates, SymbolSizes, loc, phaseType === 'R', symbol); + return ( + + + + ); + } +} + +Disband.propTypes = { + loc: PropTypes.string.isRequired, + phaseType: PropTypes.string.isRequired, + coordinates: PropTypes.object.isRequired, + symbolSizes: PropTypes.object.isRequired +}; diff --git a/diplomacy/web/src/gui/maps/common/equilateralTriangle.js b/diplomacy/web/src/gui/maps/common/equilateralTriangle.js new file mode 100644 index 0000000..8c433ea --- /dev/null +++ b/diplomacy/web/src/gui/maps/common/equilateralTriangle.js @@ -0,0 +1,122 @@ +export class EquilateralTriangle { + /** Helper class that represent an equilateral triangle.; + Used to compute intersection of a line with a side of convoy symbol, which is an equilateral triangle. **/ + constructor(x_top, y_top, x_right, y_right, x_left, y_left) { + this.x_A = x_top; + this.y_A = y_top; + this.x_B = x_right; + this.y_B = y_right; + this.x_C = x_left; + this.y_C = y_left; + this.h = this.y_B - this.y_A; + this.x_O = this.x_A; + this.y_O = this.y_A + 2 * this.h / 3; + this.line_AB_a = (this.y_B - this.y_A) / (this.x_B - this.x_A); + this.line_AB_b = this.y_B - this.x_B * this.line_AB_a; + this.line_AC_a = (this.y_C - this.y_A) / (this.x_C - this.x_A); + this.line_AC_b = this.y_C - this.x_C * this.line_AC_a; + } + + __line_OM(x_M, y_M) { + const a = (y_M - this.y_O) / (x_M - this.x_O); + const b = y_M - a * x_M; + return [a, b]; + } + + __intersection_with_AB(x_M, y_M) { + const [a, b] = [this.line_AB_a, this.line_AB_b]; + let x = null; + if (x_M === this.x_O) { + x = x_M; + } else { + const [u, v] = this.__line_OM(x_M, y_M); + if (a === u) + return [null, null]; + x = (v - b) / (a - u); + } + const y = a * x + b; + if (this.x_A <= x && x <= this.x_B && this.y_A <= y && y <= this.y_B) + return [x, y]; + return [null, null]; + } + + __intersection_with_AC(x_M, y_M) { + const [a, b] = [this.line_AC_a, this.line_AC_b]; + let x = null; + if (x_M === this.x_O) { + x = x_M; + } else { + const [u, v] = this.__line_OM(x_M, y_M); + if (a === u) + return [null, null]; + x = (v - b) / (a - u); + } + const y = a * x + b; + if (this.x_C <= x && x <= this.x_A && this.y_A <= y && y <= this.y_C) + return [x, y]; + return [null, null]; + } + + __intersection_with_BC(x_M, y_M) { + const y = this.y_C; + let x = null; + if (x_M === this.x_O) { + x = x_M; + } else { + const [a, b] = this.__line_OM(x_M, y_M); + if (a === 0) + return [null, null]; + x = (y - b) / a; + } + if (this.x_C <= x && x <= this.x_A) + return [x, y]; + return [null, null]; + } + + intersection(x_M, y_M) { + if (this.x_O === x_M && this.y_O === y_M) + return [x_M, y_M]; + if (this.x_O === x_M) { + if (y_M < this.y_O) + return [x_M, this.y_A]; + else { + // vertical line intersects BC; + return [x_M, this.y_C]; + } + } else if (this.y_O === y_M) { + let a = null; + let b = null; + if (x_M < this.x_O) { + // horizontal line intersects AC; + [a, b] = [this.line_AC_a, this.line_AC_b]; + } else { + // horizontal line intersects AB; + [a, b] = [this.line_AB_a, this.line_AB_b]; + } + const x = (y_M - b) / a; + return [x, y_M]; + } else { + // get nearest point in intersections with AB, AC, BC; + const [p1_x, p1_y] = this.__intersection_with_AB(x_M, y_M); + const [p2_x, p2_y] = this.__intersection_with_AC(x_M, y_M); + const [p3_x, p3_y] = this.__intersection_with_BC(x_M, y_M); + const distances = []; + if (p1_x !== null) { + const d1 = Math.sqrt((p1_x - x_M) * (p1_x - x_M) + (p1_y - y_M) * (p1_y - y_M)); + distances.push([d1, p1_x, p1_y]); + } + if (p2_x !== null) { + const d2 = Math.sqrt((p2_x - x_M) * (p2_x - x_M) + (p2_y - y_M) * (p2_y - y_M)); + distances.push([d2, p2_x, p2_y]); + } + if (p3_x !== null) { + const d3 = Math.sqrt((p3_x - x_M) * (p3_x - x_M) + (p3_y - y_M) * (p3_y - y_M)); + distances.push([d3, p3_x, p3_y]); + } + distances.sort(); + const output = distances[0]; + output.shift(); + return output; + } + } +} diff --git a/diplomacy/web/src/gui/maps/common/hold.js b/diplomacy/web/src/gui/maps/common/hold.js new file mode 100644 index 0000000..ce77ec5 --- /dev/null +++ b/diplomacy/web/src/gui/maps/common/hold.js @@ -0,0 +1,47 @@ +// ============================================================================== +// Copyright (C) 2019 - Philip Paquette, Steven Bocco +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License along +// with this program. If not, see . +// ============================================================================== +import React from "react"; +import {centerSymbolAroundUnit} from "./common"; +import PropTypes from "prop-types"; + +export class Hold extends React.Component { + render() { + const Coordinates = this.props.coordinates; + const Colors = this.props.colors; + const SymbolSizes = this.props.symbolSizes; + const symbol = 'HoldUnit'; + const [loc_x, loc_y] = centerSymbolAroundUnit(Coordinates, SymbolSizes, this.props.loc, false, symbol); + return ( + + + + ); + } +} + +Hold.propTypes = { + loc: PropTypes.string.isRequired, + powerName: PropTypes.string.isRequired, + coordinates: PropTypes.object.isRequired, + symbolSizes: PropTypes.object.isRequired, + colors: PropTypes.object.isRequired +}; diff --git a/diplomacy/web/src/gui/maps/common/move.js b/diplomacy/web/src/gui/maps/common/move.js new file mode 100644 index 0000000..cefb6de --- /dev/null +++ b/diplomacy/web/src/gui/maps/common/move.js @@ -0,0 +1,67 @@ +// ============================================================================== +// Copyright (C) 2019 - Philip Paquette, Steven Bocco +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License along +// with this program. If not, see . +// ============================================================================== +import React from "react"; +import {ARMY, coloredStrokeWidth, getUnitCenter, plainStrokeWidth} from "./common"; +import PropTypes from "prop-types"; + +export class Move extends React.Component { + render() { + const Coordinates = this.props.coordinates; + const SymbolSizes = this.props.symbolSizes; + const Colors = this.props.colors; + const src_loc = this.props.srcLoc; + const dest_loc = this.props.dstLoc; + const is_dislodged = this.props.phaseType === 'R'; + const [src_loc_x, src_loc_y] = getUnitCenter(Coordinates, SymbolSizes, src_loc, is_dislodged); + let [dest_loc_x, dest_loc_y] = getUnitCenter(Coordinates, SymbolSizes, dest_loc, is_dislodged); + // Adjusting destination + const delta_x = dest_loc_x - src_loc_x; + const delta_y = dest_loc_y - src_loc_y; + const vector_length = Math.sqrt(delta_x * delta_x + delta_y * delta_y); + const delta_dec = parseFloat(SymbolSizes[ARMY].width) / 2 + 2 * coloredStrokeWidth(SymbolSizes); + dest_loc_x = '' + Math.round((parseFloat(src_loc_x) + (vector_length - delta_dec) / vector_length * delta_x) * 100.) / 100.; + dest_loc_y = '' + Math.round((parseFloat(src_loc_y) + (vector_length - delta_dec) / vector_length * delta_y) * 100.) / 100.; + return ( + + + + + ); + } +} + +Move.propTypes = { + srcLoc: PropTypes.string.isRequired, + dstLoc: PropTypes.string.isRequired, + powerName: PropTypes.string.isRequired, + phaseType: PropTypes.string.isRequired, + coordinates: PropTypes.object.isRequired, + symbolSizes: PropTypes.object.isRequired, + colors: PropTypes.object.isRequired +}; diff --git a/diplomacy/web/src/gui/maps/common/supportHold.js b/diplomacy/web/src/gui/maps/common/supportHold.js new file mode 100644 index 0000000..9b350d2 --- /dev/null +++ b/diplomacy/web/src/gui/maps/common/supportHold.js @@ -0,0 +1,72 @@ +// ============================================================================== +// Copyright (C) 2019 - Philip Paquette, Steven Bocco +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License along +// with this program. If not, see . +// ============================================================================== +import React from "react"; +import {centerSymbolAroundUnit, getUnitCenter} from "./common"; +import PropTypes from "prop-types"; + +export class SupportHold extends React.Component { + render() { + const Coordinates = this.props.coordinates; + const SymbolSizes = this.props.symbolSizes; + const Colors = this.props.colors; + const loc = this.props.loc; + const dest_loc = this.props.dstLoc; + const symbol = 'SupportHoldUnit'; + const [symbol_loc_x, symbol_loc_y] = centerSymbolAroundUnit(Coordinates, SymbolSizes, dest_loc, false, symbol); + const [loc_x, loc_y] = getUnitCenter(Coordinates, SymbolSizes, loc, false); + let [dest_loc_x, dest_loc_y] = getUnitCenter(Coordinates, SymbolSizes, dest_loc, false); + + const delta_x = dest_loc_x - loc_x; + const delta_y = dest_loc_y - loc_y; + const vector_length = Math.sqrt(delta_x * delta_x + delta_y * delta_y); + const delta_dec = parseFloat(SymbolSizes[symbol].height) / 2; + dest_loc_x = '' + Math.round((parseFloat(loc_x) + (vector_length - delta_dec) / vector_length * delta_x) * 100.) / 100.; + dest_loc_y = '' + Math.round((parseFloat(loc_y) + (vector_length - delta_dec) / vector_length * delta_y) * 100.) / 100.; + + return ( + + + + + + ); + } +} + +SupportHold.propTypes = { + loc: PropTypes.string.isRequired, + dstLoc: PropTypes.string.isRequired, + powerName: PropTypes.string.isRequired, + coordinates: PropTypes.object.isRequired, + symbolSizes: PropTypes.object.isRequired, + colors: PropTypes.object.isRequired +}; diff --git a/diplomacy/web/src/gui/maps/common/supportMove.js b/diplomacy/web/src/gui/maps/common/supportMove.js new file mode 100644 index 0000000..e52e37f --- /dev/null +++ b/diplomacy/web/src/gui/maps/common/supportMove.js @@ -0,0 +1,61 @@ +// ============================================================================== +// Copyright (C) 2019 - Philip Paquette, Steven Bocco +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License along +// with this program. If not, see . +// ============================================================================== +import React from "react"; +import {ARMY, coloredStrokeWidth, getUnitCenter} from "./common"; +import PropTypes from "prop-types"; + +export class SupportMove extends React.Component { + render() { + const Coordinates = this.props.coordinates; + const SymbolSizes = this.props.symbolSizes; + const Colors = this.props.colors; + const loc = this.props.loc; + const src_loc = this.props.srcLoc; + const dest_loc = this.props.dstLoc; + const [loc_x, loc_y] = getUnitCenter(Coordinates, SymbolSizes, loc, false); + const [src_loc_x, src_loc_y] = getUnitCenter(Coordinates, SymbolSizes, src_loc, false); + let [dest_loc_x, dest_loc_y] = getUnitCenter(Coordinates, SymbolSizes, dest_loc, false); + + // Adjusting destination + const delta_x = dest_loc_x - src_loc_x; + const delta_y = dest_loc_y - src_loc_y; + const vector_length = Math.sqrt(delta_x * delta_x + delta_y * delta_y); + const delta_dec = parseFloat(SymbolSizes[ARMY].width) / 2 + 2 * coloredStrokeWidth(SymbolSizes); + dest_loc_x = '' + Math.round((parseFloat(src_loc_x) + (vector_length - delta_dec) / vector_length * delta_x) * 100.) / 100.; + dest_loc_y = '' + Math.round((parseFloat(src_loc_y) + (vector_length - delta_dec) / vector_length * delta_y) * 100.) / 100.; + return ( + + + + + ); + } +} + +SupportMove.propTypes = { + loc: PropTypes.string.isRequired, + srcLoc: PropTypes.string.isRequired, + dstLoc: PropTypes.string.isRequired, + powerName: PropTypes.string.isRequired, + coordinates: PropTypes.object.isRequired, + symbolSizes: PropTypes.object.isRequired, + colors: PropTypes.object.isRequired +}; diff --git a/diplomacy/web/src/gui/maps/common/unit.js b/diplomacy/web/src/gui/maps/common/unit.js new file mode 100644 index 0000000..277a591 --- /dev/null +++ b/diplomacy/web/src/gui/maps/common/unit.js @@ -0,0 +1,50 @@ +// ============================================================================== +// Copyright (C) 2019 - Philip Paquette, Steven Bocco +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License along +// with this program. If not, see . +// ============================================================================== +import React from "react"; +import {ARMY, FLEET} from "./common"; +import PropTypes from "prop-types"; + +export class Unit extends React.Component { + render() { + const Coordinates = this.props.coordinates; + const SymbolSizes = this.props.symbolSizes; + const split_unit = this.props.unit.split(/ +/); + const unit_type = split_unit[0]; + const loc = split_unit[1]; + const dislogged_type = this.props.isDislodged ? 'disl' : 'unit'; + const symbol = unit_type === 'F' ? FLEET : ARMY; + const loc_x = Coordinates[loc][dislogged_type][0]; + const loc_y = Coordinates[loc][dislogged_type][1]; + return ( + + ); + } +} + +Unit.propTypes = { + unit: PropTypes.string.isRequired, + powerName: PropTypes.string.isRequired, + isDislodged: PropTypes.bool.isRequired, + coordinates: PropTypes.object.isRequired, + symbolSizes: PropTypes.object.isRequired +}; diff --git a/diplomacy/web/src/gui/maps/modern/SvgModern.css b/diplomacy/web/src/gui/maps/modern/SvgModern.css new file mode 100644 index 0000000..13cccc0 --- /dev/null +++ b/diplomacy/web/src/gui/maps/modern/SvgModern.css @@ -0,0 +1,19 @@ +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/ +/* Default Text */ .SvgModern text {font-size:10pt;fill:black;font-family:Arial;} /* Text */ .SvgModern /* .credits {font-size:8pt;font-family:Arial;} */ .SvgModern /* .titletext {font-size:14px;font-weight:bolder;font-family:Arial;} */ .SvgModern .labeltext {font-size:12px;fill:black;font-family:Arial;} .SvgModern .currentnoterect {fill:#c5dfea; stroke-width:0; stroke:black;} .SvgModern .currentnotetext {font-family:serif,sans-serif; font-size:12px; fill:black; stroke:black;} .SvgModern .currentphasetext {font-family:serif,sans-serif; font-size:14px; fill:black; stroke:black;} /* Mouse Layer */ .SvgModern .invisibleContent {stroke:#000000;fill:#000000;fill-opacity:0.0;opacity:0.0} .SvgModern .provinceRed {fill:url(#patternRed); stroke:black; stroke-width: 2} .SvgModern .provinceBrown {fill:url(#patternBrown); stroke:black; stroke-width: 2} .SvgModern .provinceGreen {fill:url(#patternGreen); stroke:black; stroke-width: 2} .SvgModern .provinceBlack {fill:url(#patternBlack); stroke:black; stroke-width: 2} .SvgModern .provinceBlue {fill:url(#patternBlue); stroke:black; stroke-width: 2} /* Nations */ .SvgModern .nopower {fill:#FFEEEE; stroke:black; stroke-linejoin:round; stroke-width: 2} .SvgModern .water {fill:#DDDDFF; stroke:black; stroke-linejoin:round; stroke-width: 2} .SvgModern .britain {fill:royalblue;stroke:black;; stroke-width: 2} .SvgModern .egypt {fill:#808000;stroke:black;; stroke-width: 2} .SvgModern .france {fill:#00FFFF;stroke:black;; stroke-width: 2} .SvgModern .germany {fill:darkgrey;stroke:black;; stroke-width: 2} .SvgModern .italy {fill:#80FF80;stroke:black;; stroke-width: 2} .SvgModern .poland {fill:#FF0000;stroke:black;; stroke-width: 2} .SvgModern .russia {fill:#008000;stroke:black;; stroke-width: 2} .SvgModern .spain {fill:#FF8080;stroke:black;; stroke-width: 2} .SvgModern .turkey {fill:#FFFF00;stroke:black;; stroke-width: 2} .SvgModern .ukraine {fill:#FF00FF;stroke:black;; stroke-width: 2} /* Unit Colors */ .SvgModern .unitbritain {fill:deepskyblue;stroke:black;fill-opacity:0.90;} .SvgModern .unitegypt {fill:#808000;stroke:black;fill-opacity:0.90;} .SvgModern .unitfrance {fill:#00FFFF;stroke:black;fill-opacity:0.90;} .SvgModern .unitgermany {fill:darkgrey;stroke:black;fill-opacity:0.90;} .SvgModern .unititaly {fill:#80FF80;stroke:black;fill-opacity:0.90;} .SvgModern .unitpoland {fill:#FF0000;stroke:black;fill-opacity:0.90;} .SvgModern .unitrussia {fill:#008000;stroke:black;fill-opacity:0.90;} .SvgModern .unitspain {fill:#FF8080;stroke:black;fill-opacity:0.90;} .SvgModern .unitturkey {fill:#FFFF00;stroke:black;fill-opacity:0.90;} .SvgModern .unitukraine {fill:#FF00FF;stroke:black;fill-opacity:0.90;} /* order drawing styles, stroke and fill colors should not be specified */ .SvgModern .supportorder {stroke-width:2; fill:none; stroke-dasharray:5,5;} .SvgModern .convoyorder {stroke-dasharray:15,5; stroke-width:2; fill:none;} .SvgModern .shadowdash {stroke-width:4; fill:none; stroke:black; opacity:0.45;} .SvgModern .varwidthorder {fill:none;} .SvgModern .varwidthshadow {fill:none; stroke:black;} .SvgModern .style1 {fill:darkGray} \ No newline at end of file diff --git a/diplomacy/web/src/gui/maps/modern/SvgModern.js b/diplomacy/web/src/gui/maps/modern/SvgModern.js new file mode 100644 index 0000000..32fd9fd --- /dev/null +++ b/diplomacy/web/src/gui/maps/modern/SvgModern.js @@ -0,0 +1,20 @@ +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/ +/** Generated with parameters: Namespace(input='src/diplomacy/maps/svg/modern.svg', name='SvgModern', output='src/gui/maps/modern/') **/ + import React from 'react'; import PropTypes from 'prop-types'; import './SvgModern.css'; import {Coordinates, SymbolSizes, Colors} from "./SvgModernMetadata"; import {getClickedID, parseLocation, setInfluence} from "../common/common"; import {Game} from "../../../diplomacy/engine/game"; import {MapData} from "../../utils/map_data"; import {UTILS} from "../../../diplomacy/utils/utils"; import {Diplog} from "../../../diplomacy/utils/diplog"; import {extendOrderBuilding} from "../../utils/order_building"; import {Unit} from "../common/unit"; import {Hold} from "../common/hold"; import {Move} from "../common/move"; import {SupportMove} from "../common/supportMove"; import {SupportHold} from "../common/supportHold"; import {Convoy} from "../common/convoy"; import {Build} from "../common/build"; import {Disband} from "../common/disband"; export class SvgModern extends React.Component { constructor(props) { super(props); this.onClick = this.onClick.bind(this); this.onHover = this.onHover.bind(this); } onClick(event) { if (this.props.orderBuilding) return this.handleClickedID(getClickedID(event)); } onHover(event) { return this.handleHoverID(getClickedID(event)); } handleClickedID(id) { const orderBuilding = this.props.orderBuilding; if (!orderBuilding.builder) return this.props.onError('No orderable locations.'); const province = this.props.mapData.getProvince(id); if (!province) throw new Error(`Cannot find a province named ${id}`); const stepLength = orderBuilding.builder.steps.length; if (orderBuilding.path.length >= stepLength) throw new Error(`Order building: current steps count (${orderBuilding.path.length}) should be less than expected steps count (${stepLength}) (${orderBuilding.path.join(', ')}).`); const lengthAfterClick = orderBuilding.path.length + 1; let validLocations = []; const testedPath = [orderBuilding.type].concat(orderBuilding.path); const value = UTILS.javascript.getTreeValue(this.props.game.ordersTree, testedPath); if (value !== null) { const checker = orderBuilding.builder.steps[lengthAfterClick - 1]; try { const possibleLocations = checker(province, orderBuilding.power); for (let possibleLocation of possibleLocations) { possibleLocation = possibleLocation.toUpperCase(); if (value.includes(possibleLocation)) validLocations.push(possibleLocation); } } catch (error) { return this.props.onError(error); } } if (!validLocations.length) return this.props.onError('Disallowed.'); if (validLocations.length > 1 && orderBuilding.type === 'S' && orderBuilding.path.length >= 2) { /* We are building a support order and we have a multiple choice for a location. */ /* Let's check if next location to choose is a coast. To have a coast: */ /* - all possible locations must start with same 3 characters. */ /* - we expect at least province name in possible locations (e.g. 'SPA' for 'SPA/NC'). */ /* If we have a coast, we will remove province name from possible locations. */ let isACoast = true; let validLocationsNoProvinceName = []; for (let i = 0; i < validLocations.length; ++i) { let location = validLocations[i]; if (i > 0) { /* Compare 3 first letters with previous location. */ if (validLocations[i - 1].substring(0, 3).toUpperCase() !== validLocations[i].substring(0, 3).toUpperCase()) { /* No same prefix with previous location. We does not have a coast. */ isACoast = false; break; } } if (location.length !== 3) validLocationsNoProvinceName.push(location); } if (validLocations.length === validLocationsNoProvinceName.length) { /* We have not found province name. */ isACoast = false; } if (isACoast) { /* We want to choose location in a coastal province. Let's remove province name. */ validLocations = validLocationsNoProvinceName; } } if (validLocations.length > 1) { if (this.props.onSelectLocation) { return this.props.onSelectLocation(validLocations, orderBuilding.power, orderBuilding.type, orderBuilding.path); } else { Diplog.warn(`Forced to select first valid location.`); validLocations = [validLocations[0]]; } } let orderBuildingType = orderBuilding.type; if (lengthAfterClick === stepLength && orderBuildingType === 'M') { const moveOrderPath = ['M'].concat(orderBuilding.path, validLocations[0]); const moveTypes = UTILS.javascript.getTreeValue(this.props.game.ordersTree, moveOrderPath); if (moveTypes !== null) { if (moveTypes.length === 2 && this.props.onSelectVia) { /* This move can be done either regularly or VIA a fleet. Let user choose. */ return this.props.onSelectVia(validLocations[0], orderBuilding.power, orderBuilding.path); } else { orderBuildingType = moveTypes[0]; } } } extendOrderBuilding( orderBuilding.power, orderBuildingType, orderBuilding.path, validLocations[0], this.props.onOrderBuilding, this.props.onOrderBuilt, this.props.onError ); } handleHoverID(id) { if (this.props.onHover) { const province = this.props.mapData.getProvince(id); if (province) { this.props.onHover(province.name, this.getRelatedOrders(province.name)); } } } getRelatedOrders(name) { const orders = []; if (this.props.orders) { for (let powerOrders of Object.values(this.props.orders)) { if (powerOrders) { for (let order of powerOrders) { const pieces = order.split(/ +/); if (pieces[1].slice(0, 3) === name.toUpperCase().slice(0, 3)) orders.push(order); } } } } return orders; } getNeighbors(extraLocation) { const selectedPath = [this.props.orderBuilding.type].concat(this.props.orderBuilding.path); if (extraLocation) selectedPath.push(extraLocation); const possibleNeighbors = UTILS.javascript.getTreeValue(this.props.game.ordersTree, selectedPath); const neighbors = possibleNeighbors ? possibleNeighbors.map(neighbor => parseLocation(neighbor)) : []; return neighbors.length ? neighbors: null; } render() { const classes = {"_ada":"nopower","_adr":"water","_aeg":"water","_alb":"nopower","_ale":"nopower","_alg":"nopower","_als":"nopower","_ana":"nopower","_adl":"nopower","_ank":"nopower","_apu":"nopower","_ara":"water","_arc":"water","_arm":"nopower","_asw":"nopower","_aus":"nopower","_auv":"nopower","_aze":"nopower","_bal":"water","_bar":"nopower","_bel":"nopower","_ber":"nopower","_bhm":"water","_bie":"nopower","_bis":"water","_bor":"nopower","_bos":"nopower","_bri":"nopower","_brn":"water","_bul":"nopower","_cai":"nopower","_cas":"water","_cau":"nopower","_crp":"nopower","_cly":"nopower","_cro":"nopower","_cze":"nopower","_den":"nopower","_don":"nopower","_ebs":"water","_edi":"nopower","_eme":"water","_eng":"water","_esa":"nopower","_est":"nopower","_fin":"nopower","_fra":"nopower","_gda":"nopower","_geo":"nopower","_gib":"nopower","_gob":"water","_gol":"water","_gor":"nopower","_gre":"nopower","_ham":"nopower","_hel":"water","_hol":"nopower","_hun":"nopower","_ice":"nopower","_ion":"water","_ire":"nopower","_iri":"water","_irk":"nopower","_irn":"nopower","_isr":"nopower","_ist":"nopower","_izm":"nopower","_jor":"nopower","_kaz":"nopower","_kha":"nopower","_kie":"nopower","_kra":"nopower","_lap":"nopower","_lat":"nopower","_lbn":"water","_lib":"nopower","_lig":"nopower","_lit":"nopower","_lpl":"nopower","_lon":"nopower","_lyo":"nopower","_mac":"nopower","_mad":"nopower","_mal":"water","_mar":"nopower","_mat":"water","_mil":"nopower","_mol":"nopower","_mon":"nopower","_mor":"nopower","_mos":"nopower","_mun":"nopower","_mur":"nopower","_nap":"nopower","_nat":"water","_nav":"nopower","_nwy":"nopower","_nth":"water","_nwg":"water","_ode":"nopower","_par":"nopower","_per":"water","_pic":"nopower","_pie":"nopower","_pod":"nopower","_por":"nopower","_pru":"nopower","_red":"water","_rom":"nopower","_ros":"nopower","_ruh":"nopower","_rum":"nopower","_sat":"water","_sau":"nopower","_sax":"nopower","_ser":"nopower","_sev":"nopower","_sib":"nopower","_sil":"nopower","_sin":"nopower","_ska":"water","_slk":"nopower","_stp":"nopower","_sog":"water","_svl":"nopower","_swe":"nopower","_swi":"nopower","_syr":"nopower","_tun":"nopower","_tus":"nopower","_tyr":"water","_ura":"nopower","_ven":"nopower","_vol":"nopower","_wal":"nopower","_war":"nopower","_wbs":"water","_wme":"water","_wsa":"nopower","_whi":"water","_yor":"nopower","BriefLabelLayer":"labeltext","CurrentNote":"currentnotetext","CurrentNote2":"currentnotetext","CurrentPhase":"currentphasetext","MouseLayer":"invisibleContent"}; const game = this.props.game; const mapData = this.props.mapData; const orders = this.props.orders; /* Current phase. */ const current_phase = (game.phase[0] === '?' || game.phase === 'COMPLETED') ? 'FINAL' : game.phase; /* Notes. */ const nb_centers = []; for (let power of Object.values(game.powers)) { if (!power.isEliminated()) nb_centers.push([power.name.substr(0, 3), power.centers.length]); } /* Sort nb_centers by descending number of centers. */ nb_centers.sort((a, b) => { return -(a[1] - b[1]) || a[0].localeCompare(b[0]); }); const nb_centers_per_power = nb_centers.map((couple) => (couple[0] + ': ' + couple[1])).join(' '); const note = game.note; /* Adding units, influence and orders. */ const renderedUnits = []; const renderedDislodgedUnits = []; const renderedOrders = []; const renderedOrders2 = []; const renderedHighestOrders = []; for (let power of Object.values(game.powers)) if (!power.isEliminated()) { for (let unit of power.units) { renderedUnits.push( ); } for (let unit of Object.keys(power.retreats)) { renderedDislodgedUnits.push( ); } for (let center of power.centers) { setInfluence(classes, mapData, center, power.name); } for (let loc of power.influence) { if (!mapData.supplyCenters.has(loc)) setInfluence(classes, mapData, loc, power.name); } if (orders) { const powerOrders = (orders && orders.hasOwnProperty(power.name) && orders[power.name]) || []; for (let order of powerOrders) { const tokens = order.split(/ +/); if (!tokens || tokens.length < 3) continue; const unit_loc = tokens[1]; if (tokens[2] === 'H') { renderedOrders.push( ); } else if (tokens[2] === '-') { const destLoc = tokens[tokens.length - (tokens[tokens.length - 1] === 'VIA' ? 2 : 1)]; renderedOrders.push( ); } else if (tokens[2] === 'S') { const destLoc = tokens[tokens.length - 1]; if (tokens.includes('-')) { const srcLoc = tokens[4]; renderedOrders2.push( ); } else { renderedOrders2.push( ); } } else if (tokens[2] === 'C') { const srcLoc = tokens[4]; const destLoc = tokens[tokens.length - 1]; if ((srcLoc !== destLoc) && (tokens.includes('-'))) { renderedOrders2.push( ); } } else if (tokens[2] === 'B') { renderedHighestOrders.push( ); } else if (tokens[2] === 'D') { renderedHighestOrders.push( ); } else if (tokens[2] === 'R') { const destLoc = tokens[3]; renderedOrders.push( ); } else { throw new Error(`Unknown error to render (${order}).`); } } } } if (this.props.orderBuilding && this.props.orderBuilding.path.length) { const clicked = parseLocation(this.props.orderBuilding.path[0]); const province = this.props.mapData.getProvince(clicked); if (!province) throw new Error(('Unknown clicked province ' + clicked)); const clickedID = province.getID(classes); if (!clicked) throw new Error(`Unknown path (${clickedID}) for province (${clicked}).`); classes[clickedID] = 'provinceRed'; const neighbors = this.getNeighbors(); if (neighbors) { for (let neighbor of neighbors) { const neighborProvince = this.props.mapData.getProvince(neighbor); if (!neighborProvince) throw new Error('Unknown neighbor province ' + neighbor); const neighborID = neighborProvince.getID(classes); if (!neighborID) throw new Error(`Unknown neoghbor path (${neighborID}) for province (${neighbor}).`); classes[neighborID] = neighborProvince.isWater() ? 'provinceBlue' : 'provinceGreen'; } } } if (this.props.showAbbreviations === false) { classes['BriefLabelLayer'] = 'visibilityHidden'; } return ( MODERN nc sc {renderedOrders2} {renderedOrders} {renderedUnits} {renderedDislodgedUnits} {renderedHighestOrders} ada adr aeg alb ale alg als ana adl ank apu ara arc arm asw aus auv aze bal bar bel ber bhm bie bis bor bos bri brn bul cai cas cau crp cly cro cze den don ebs edi eme eng esa est fin fra gda geo gib gob gol gor gre ham hel hol hun ice ion ire iri irk irn isr ist izm jor kaz kha kie kra lap lat lbn lib lig lit lpl lon lyo mac mad mal mar mat mil mol mon mor mos mun mur nap nat nav nwy nth nwg ode par per pic pie pod por pru red rom ros ruh rum sat sau sax ser sev sib sil sin ska slk stp sog svl swe swi syr tun tus tyr ura ven vol wal war wbs wme wsa whi yor {nb_centers_per_power ? nb_centers_per_power : ''} {note ? note : ''} {current_phase} ); } } SvgModern.propTypes = { game: PropTypes.instanceOf(Game).isRequired, mapData: PropTypes.instanceOf(MapData).isRequired, orders: PropTypes.object, onHover: PropTypes.func, onError: PropTypes.func.isRequired, onSelectLocation: PropTypes.func, onSelectVia: PropTypes.func, onOrderBuilding: PropTypes.func, onOrderBuilt: PropTypes.func, orderBuilding: PropTypes.object, showAbbreviations: PropTypes.bool }; // eslint-disable-line semi \ No newline at end of file diff --git a/diplomacy/web/src/gui/maps/modern/SvgModernMetadata.js b/diplomacy/web/src/gui/maps/modern/SvgModernMetadata.js new file mode 100644 index 0000000..29b4a20 --- /dev/null +++ b/diplomacy/web/src/gui/maps/modern/SvgModernMetadata.js @@ -0,0 +1,21 @@ +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/ +export const Coordinates = {"ADA":{"unit":["527.9","356.9"],"disl":["531.9","352.9"]},"ADL":{"unit":["83.1","359.8"],"disl":["87.1","355.8"]},"ADR":{"unit":["276.8","332.9"],"disl":["280.8","328.9"]},"AEG":{"unit":["371.2","385.3"],"disl":["375.2","381.3"]},"ALB":{"unit":["314.4","350.6"],"disl":["318.4","346.6"]},"ALE":{"unit":["404.8","502.3"],"disl":["408.8","498.3"]},"ALG":{"unit":["130.4","444.2"],"disl":["134.4","440.2"]},"ALS":{"unit":["187.8","238.0"],"disl":["191.8","234.0"]},"ANA":{"unit":["455.3","379.0"],"disl":["459.3","375.0"]},"ANK":{"unit":["455.3","339.1"],"disl":["459.3","335.1"]},"APU":{"unit":["269.5","345.5"],"disl":["273.5","341.5"]},"ARA":{"unit":["674.0","520.6"],"disl":["678.0","516.6"]},"ARC":{"unit":["196.3","5.8"],"disl":["200.3","1.8"]},"ARM":{"unit":["562.4","322.9"],"disl":["566.4","318.9"]},"ASW":{"unit":["446.2","539.4"],"disl":["450.2","535.4"]},"AUS":{"unit":["268.9","268.5"],"disl":["272.9","264.5"]},"AUV":{"unit":["146.4","300.5"],"disl":["150.4","296.5"]},"AZE":{"unit":["591.6","312.5"],"disl":["595.6","308.5"]},"BAL":{"unit":["310.0","136.4"],"disl":["314.0","132.4"]},"BAR":{"unit":["113.6","322.2"],"disl":["117.6","318.2"]},"BEL":{"unit":["177.8","206.8"],"disl":["181.8","202.8"]},"BER":{"unit":["260.8","188.9"],"disl":["264.8","184.9"]},"BHM":{"unit":["264.1","165.6"],"disl":["268.1","161.6"]},"BIE":{"unit":["366.0","189.8"],"disl":["370.0","185.8"]},"BIS":{"unit":["86.2","239.3"],"disl":["90.2","235.3"]},"BOR":{"unit":["133.6","278.1"],"disl":["137.6","274.1"]},"BOS":{"unit":["294.7","307.0"],"disl":["298.7","303.0"]},"BRI":{"unit":["126.2","232.9"],"disl":["130.2","228.9"]},"BRN":{"unit":["287.1","4.4"],"disl":["291.1","0.4"]},"BUL":{"unit":["380.7","328.2"],"disl":["384.7","324.2"]},"CAI":{"unit":["475.3","503.1"],"disl":["479.3","499.1"]},"CAS":{"unit":["626.7","311.7"],"disl":["630.7","307.7"]},"CAU":{"unit":["552.9","273.7"],"disl":["556.9","269.7"]},"CLY":{"unit":["144.0","94.6"],"disl":["148.0","90.6"]},"CRO":{"unit":["280.7","287.1"],"disl":["284.7","283.1"]},"CRP":{"unit":["434.3","183.5"],"disl":["438.3","179.5"]},"CZE":{"unit":["277.5","240.0"],"disl":["281.5","236.0"]},"DEN":{"unit":["242.2","147.7"],"disl":["246.2","143.7"]},"DON":{"unit":["476.3","245.3"],"disl":["480.3","241.3"]},"EBS":{"unit":["475.2","306.9"],"disl":["479.2","302.9"]},"EDI":{"unit":["159.9","111.9"],"disl":["163.9","107.9"]},"EME":{"unit":["412.3","434.5"],"disl":["416.3","430.5"]},"ENG":{"unit":["71.6","206.8"],"disl":["75.6","202.8"]},"ESA":{"unit":["355.4","475.7"],"disl":["359.4","471.7"]},"EST":{"unit":["359.7","111.2"],"disl":["363.7","107.2"]},"FIN":{"unit":["356.5","62.5"],"disl":["360.5","58.5"]},"FRA":{"unit":["233.6","212.8"],"disl":["237.6","208.8"]},"GDA":{"unit":["325.9","173.6"],"disl":["329.9","169.6"]},"GEO":{"unit":["534.2","302.1"],"disl":["538.2","298.1"]},"GIB":{"unit":["50.5","366.1"],"disl":["54.5","362.1"]},"GOB":{"unit":["316.6","97.9"],"disl":["320.6","93.9"]},"GOL":{"unit":["135.7","346.4"],"disl":["139.7","342.4"]},"GOR":{"unit":["522.6","93.0"],"disl":["526.6","89.0"]},"GRE":{"unit":["335.5","367.5"],"disl":["339.5","363.5"]},"HAM":{"unit":["227.6","190.2"],"disl":["231.6","186.2"]},"HEL":{"unit":["214.3","163.0"],"disl":["218.3","159.0"]},"HOL":{"unit":["196.4","188.2"],"disl":["200.4","184.2"]},"HUN":{"unit":["309.3","275.2"],"disl":["313.3","271.2"]},"ICE":{"unit":["113.6","22.6"],"disl":["117.6","18.6"]},"ION":{"unit":["302.9","391.6"],"disl":["306.9","387.6"]},"IRE":{"unit":["96.2","139.8"],"disl":["100.2","135.8"]},"IRI":{"unit":["96.8","172.3"],"disl":["100.8","168.3"]},"IRK":{"unit":["569.9","387.4"],"disl":["573.9","383.4"]},"IRN":{"unit":["624.6","385.3"],"disl":["628.6","381.3"]},"IRN/NC":{"unit":["639.2","357.5"],"disl":["643.2","353.5"]},"IRN/SC":{"unit":["679.2","446.8"],"disl":["683.2","442.8"]},"ISR":{"unit":["494.2","424.0"],"disl":["498.2","420.0"]},"IST":{"unit":["422.7","355.9"],"disl":["426.7","351.9"]},"IZM":{"unit":["398.5","381.1"],"disl":["402.5","377.1"]},"JOR":{"unit":["512.1","453.3"],"disl":["516.1","449.3"]},"KAZ":{"unit":["644.6","198.2"],"disl":["648.6","194.2"]},"KHA":{"unit":["478.5","224.4"],"disl":["482.5","220.4"]},"KIE":{"unit":["428.0","214.6"],"disl":["432.0","210.6"]},"KRA":{"unit":["334.4","221.3"],"disl":["338.4","217.3"]},"LAP":{"unit":["332.3","7.1"],"disl":["336.3","3.1"]},"LAT":{"unit":["361.2","137.7"],"disl":["365.2","133.7"]},"LBN":{"unit":["306.5","450.5"],"disl":["310.5","446.5"]},"LIB":{"unit":["301.8","501.0"],"disl":["305.8","497.0"]},"LIG":{"unit":["194.5","323.0"],"disl":["198.5","319.0"]},"LIT":{"unit":["347.1","161.0"],"disl":["351.1","157.0"]},"LIV":{"unit":["134.7","137.8"],"disl":["138.7","133.8"]},"LON":{"unit":["160.6","182.9"],"disl":["164.6","178.9"]},"LYO":{"unit":["164.5","264.0"],"disl":["168.5","260.0"]},"MAC":{"unit":["335.3","344.0"],"disl":["339.3","340.0"]},"MAD":{"unit":["66.9","318.0"],"disl":["70.9","314.0"]},"MAL":{"unit":["255.6","440.0"],"disl":["259.6","436.0"]},"MAR":{"unit":["171.2","292.4"],"disl":["175.2","288.4"]},"MID":{"unit":["13.7","211.8"],"disl":["17.7","207.8"]},"MIL":{"unit":["227.0","283.8"],"disl":["231.0","279.8"]},"MOL":{"unit":["391.1","262.2"],"disl":["395.1","258.2"]},"MON":{"unit":["187.1","301.7"],"disl":["191.1","297.7"]},"MOR":{"unit":["45.3","411.6"],"disl":["49.3","407.6"]},"MOS":{"unit":["434.8","151.0"],"disl":["438.8","147.0"]},"MUN":{"unit":["224.5","248.0"],"disl":["228.5","244.0"]},"MUR":{"unit":["451.5","51.8"],"disl":["455.5","47.8"]},"NAO":{"unit":["52.6","107.8"],"disl":["56.6","103.8"]},"NAP":{"unit":["270.1","365.4"],"disl":["274.1","361.4"]},"NAV":{"unit":["99.9","298.1"],"disl":["103.9","294.1"]},"NTH":{"unit":["186.5","127.8"],"disl":["190.5","123.8"]},"NWG":{"unit":["188.5","74.7"],"disl":["192.5","70.7"]},"NWY":{"unit":["232.3","101.2"],"disl":["236.3","97.2"]},"ODE":{"unit":["406.9","241.2"],"disl":["410.9","237.2"]},"PAR":{"unit":["149.4","243.9"],"disl":["153.4","239.9"]},"PER":{"unit":["662.4","463.1"],"disl":["666.4","459.1"]},"PIC":{"unit":["159.3","220.8"],"disl":["163.3","216.8"]},"PIE":{"unit":["205.1","291.1"],"disl":["209.1","287.1"]},"POD":{"unit":["370.2","232.4"],"disl":["374.2","228.4"]},"POR":{"unit":["30.5","315.9"],"disl":["34.5","311.9"]},"PRU":{"unit":["294.7","181.6"],"disl":["298.7","177.6"]},"RED":{"unit":["522.6","521.0"],"disl":["526.6","517.0"]},"ROM":{"unit":["244.9","333.6"],"disl":["248.9","329.6"]},"ROS":{"unit":["493.2","270.3"],"disl":["497.2","266.3"]},"RUH":{"unit":["206.4","210.1"],"disl":["210.4","206.1"]},"RUM":{"unit":["359.1","285.8"],"disl":["363.1","281.8"]},"SAO":{"unit":["4.3","389.5"],"disl":["8.3","385.5"]},"SAU":{"unit":["569.9","505.2"],"disl":["573.9","501.2"]},"SAX":{"unit":["245.6","232.7"],"disl":["249.6","228.7"]},"SER":{"unit":["326.5","315.0"],"disl":["330.5","311.0"]},"SEV":{"unit":["443.8","270.7"],"disl":["447.8","266.7"]},"SIB":{"unit":["635.1","87.8"],"disl":["639.1","83.8"]},"SIL":{"unit":["293.5","214.0"],"disl":["297.5","210.0"]},"SIN":{"unit":["485.1","468.4"],"disl":["489.1","464.4"]},"SKA":{"unit":["248.9","119.8"],"disl":["252.9","115.8"]},"SLK":{"unit":["313.3","251.9"],"disl":["317.3","247.9"]},"SOG":{"unit":["64.5","387.0"],"disl":["68.5","383.0"]},"STP":{"unit":["390.1","110.9"],"disl":["394.1","106.9"]},"SVE":{"unit":["50.5","339.0"],"disl":["54.5","335.0"]},"SWE":{"unit":["276.6","124.6"],"disl":["280.6","120.6"]},"SWI":{"unit":["206.5","268.0"],"disl":["210.5","264.0"]},"SYR":{"unit":["528.2","395.3"],"disl":["532.2","391.3"]},"TUN":{"unit":["204.0","446.3"],"disl":["208.0","442.3"]},"TUS":{"unit":["225.6","311.7"],"disl":["229.6","307.7"]},"TYR":{"unit":["234.2","374.7"],"disl":["238.2","370.7"]},"URA":{"unit":["526.8","16.3"],"disl":["530.8","12.3"]},"VEN":{"unit":["249.6","309.7"],"disl":["253.6","305.7"]},"VOL":{"unit":["514.2","192.9"],"disl":["518.2","188.9"]},"WAL":{"unit":["130.0","188.9"],"disl":["134.0","184.9"]},"WAR":{"unit":["316.6","203.5"],"disl":["320.6","199.5"]},"WBS":{"unit":["423.6","309.6"],"disl":["427.6","305.6"]},"WHI":{"unit":["422.7","9.9"],"disl":["426.7","5.9"]},"WME":{"unit":["143.0","380.1"],"disl":["147.0","376.1"]},"WSA":{"unit":["47.4","511.5"],"disl":["51.4","507.5"]},"YOR":{"unit":["154.6","153.7"],"disl":["158.6","149.7"]}}; +export const SymbolSizes = {"Fleet":{"width":16.0,"height":16.0},"Army":{"width":16.0,"height":16.0},"Wing":{"width":20.0,"height":20.0},"DislodgedFleet":{"width":20.0,"height":20.0},"DislodgedArmy":{"width":20.0,"height":20.0},"DislodgedWing":{"width":20.0,"height":20.0},"FailedOrder":{"width":17.0,"height":17.0},"SupplyCenter":{"width":5.0,"height":5.0},"BuildUnit":{"width":24.0,"height":24.0},"RemoveUnit":{"width":24.0,"height":24.0},"WaivedBuild":{"width":25.0,"height":25.0},"HoldUnit":{"width":30.0,"height":30.0},"SupportHoldUnit":{"width":34.5,"height":34.5},"ConvoyTriangle":{"width":40.0,"height":34.5},"Stroke":{"width":2.0,"height":4.0}}; +export const Colors = {"BRITAIN":"royalblue","EGYPT":"#808000","FRANCE":"#00FFFF","GERMANY":"darkgrey","ITALY":"#80FF80","POLAND":"#FF0000","RUSSIA":"#008000","SPAIN":"#FF8080","TURKEY":"#FFFF00","UKRAINE":"#FF00FF"}; diff --git a/diplomacy/web/src/gui/maps/pure/SvgPure.css b/diplomacy/web/src/gui/maps/pure/SvgPure.css new file mode 100644 index 0000000..2aa9fda --- /dev/null +++ b/diplomacy/web/src/gui/maps/pure/SvgPure.css @@ -0,0 +1,19 @@ +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/ +/* text */ .SvgPure svg { font-size: 100% } .SvgPure .labeltext {stroke-width:0.1; stroke:black; fill:black;} .SvgPure .currentnotetext {font-family:serif,sans-serif; font-size:1.5em; fill:black; stroke:black;} .SvgPure .currentphasetext {font-family:serif,sans-serif; fill:black; stroke:black;} /* invisible click rects fill:none does not work */ .SvgPure .invisibleContent {stroke:#000000; fill:#000000; fill-opacity:0.0; opacity:0.0} /* default region coloring, by power */ .SvgPure .provinceRed {fill:url(#patternRed); stroke: black; stroke-width: 2} .SvgPure .provinceBrown {fill:url(#patternBrown); stroke: black; stroke-width: 2} .SvgPure .provinceGreen {fill:url(#patternGreen); stroke: black; stroke-width: 2} .SvgPure .provinceBlack {fill:url(#patternBlack); stroke: black; stroke-width: 2} .SvgPure .provinceBlue {fill:url(#patternBlue); stroke: black; stroke-width: 2} .SvgPure .nopower {fill:antiquewhite; stroke:#000000; stroke-width:2} .SvgPure .water {fill:#c5dfea; stroke:#000000; stroke-width:2} .SvgPure .austria {fill:#c48f85; stroke:#000000; stroke-width:2} .SvgPure .england {fill:darkviolet; stroke:#000000; stroke-width:2} .SvgPure .france {fill:royalblue; stroke:#000000; stroke-width:2} .SvgPure .germany {fill:#a08a75; stroke:#000000; stroke-width:2} .SvgPure .italy {fill:forestgreen; stroke:#000000; stroke-width:2} .SvgPure .russia {fill:#757d91; stroke:#000000; stroke-width:2} .SvgPure .turkey {fill:#b9a61c; stroke:#000000; stroke-width:2} /* unit colors, by power note that underscores are not supported */ .SvgPure .unitaustria {fill:red; fill-opacity:0.85} .SvgPure .unitengland {fill:mediumpurple; fill-opacity:0.85} .SvgPure .unitfrance {fill:deepskyblue; fill-opacity:0.85} .SvgPure .unitgermany {fill:dimgray; fill-opacity:0.85} .SvgPure .unititaly {fill:olive; fill-opacity:0.85} .SvgPure .unitrussia {fill:white; fill-opacity:1.0} .SvgPure .unitturkey {fill:yellow; fill-opacity:0.85} /* order drawing styles, stroke and fill colors should not be specified */ .SvgPure .supportorder {stroke-width:6; fill:none; stroke-dasharray:5,5;} .SvgPure .convoyorder {stroke-dasharray:15,5; stroke-width:6; fill:none;} .SvgPure .shadowdash {stroke-width:10; fill:none; stroke:black; opacity:0.45;} .SvgPure .varwidthorder {fill:none;} .SvgPure .varwidthshadow {fill:none; stroke:black;} \ No newline at end of file diff --git a/diplomacy/web/src/gui/maps/pure/SvgPure.js b/diplomacy/web/src/gui/maps/pure/SvgPure.js new file mode 100644 index 0000000..9407348 --- /dev/null +++ b/diplomacy/web/src/gui/maps/pure/SvgPure.js @@ -0,0 +1,20 @@ +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/ +/** Generated with parameters: Namespace(input='src/diplomacy/maps/svg/pure.svg', name='SvgPure', output='src/gui/maps/pure/') **/ + import React from 'react'; import PropTypes from 'prop-types'; import './SvgPure.css'; import {Coordinates, SymbolSizes, Colors} from "./SvgPureMetadata"; import {getClickedID, parseLocation, setInfluence} from "../common/common"; import {Game} from "../../../diplomacy/engine/game"; import {MapData} from "../../utils/map_data"; import {UTILS} from "../../../diplomacy/utils/utils"; import {Diplog} from "../../../diplomacy/utils/diplog"; import {extendOrderBuilding} from "../../utils/order_building"; import {Unit} from "../common/unit"; import {Hold} from "../common/hold"; import {Move} from "../common/move"; import {SupportMove} from "../common/supportMove"; import {SupportHold} from "../common/supportHold"; import {Convoy} from "../common/convoy"; import {Build} from "../common/build"; import {Disband} from "../common/disband"; export class SvgPure extends React.Component { constructor(props) { super(props); this.onClick = this.onClick.bind(this); this.onHover = this.onHover.bind(this); } onClick(event) { if (this.props.orderBuilding) return this.handleClickedID(getClickedID(event)); } onHover(event) { return this.handleHoverID(getClickedID(event)); } handleClickedID(id) { const orderBuilding = this.props.orderBuilding; if (!orderBuilding.builder) return this.props.onError('No orderable locations.'); const province = this.props.mapData.getProvince(id); if (!province) throw new Error(`Cannot find a province named ${id}`); const stepLength = orderBuilding.builder.steps.length; if (orderBuilding.path.length >= stepLength) throw new Error(`Order building: current steps count (${orderBuilding.path.length}) should be less than expected steps count (${stepLength}) (${orderBuilding.path.join(', ')}).`); const lengthAfterClick = orderBuilding.path.length + 1; let validLocations = []; const testedPath = [orderBuilding.type].concat(orderBuilding.path); const value = UTILS.javascript.getTreeValue(this.props.game.ordersTree, testedPath); if (value !== null) { const checker = orderBuilding.builder.steps[lengthAfterClick - 1]; try { const possibleLocations = checker(province, orderBuilding.power); for (let possibleLocation of possibleLocations) { possibleLocation = possibleLocation.toUpperCase(); if (value.includes(possibleLocation)) validLocations.push(possibleLocation); } } catch (error) { return this.props.onError(error); } } if (!validLocations.length) return this.props.onError('Disallowed.'); if (validLocations.length > 1 && orderBuilding.type === 'S' && orderBuilding.path.length >= 2) { /* We are building a support order and we have a multiple choice for a location. */ /* Let's check if next location to choose is a coast. To have a coast: */ /* - all possible locations must start with same 3 characters. */ /* - we expect at least province name in possible locations (e.g. 'SPA' for 'SPA/NC'). */ /* If we have a coast, we will remove province name from possible locations. */ let isACoast = true; let validLocationsNoProvinceName = []; for (let i = 0; i < validLocations.length; ++i) { let location = validLocations[i]; if (i > 0) { /* Compare 3 first letters with previous location. */ if (validLocations[i - 1].substring(0, 3).toUpperCase() !== validLocations[i].substring(0, 3).toUpperCase()) { /* No same prefix with previous location. We does not have a coast. */ isACoast = false; break; } } if (location.length !== 3) validLocationsNoProvinceName.push(location); } if (validLocations.length === validLocationsNoProvinceName.length) { /* We have not found province name. */ isACoast = false; } if (isACoast) { /* We want to choose location in a coastal province. Let's remove province name. */ validLocations = validLocationsNoProvinceName; } } if (validLocations.length > 1) { if (this.props.onSelectLocation) { return this.props.onSelectLocation(validLocations, orderBuilding.power, orderBuilding.type, orderBuilding.path); } else { Diplog.warn(`Forced to select first valid location.`); validLocations = [validLocations[0]]; } } let orderBuildingType = orderBuilding.type; if (lengthAfterClick === stepLength && orderBuildingType === 'M') { const moveOrderPath = ['M'].concat(orderBuilding.path, validLocations[0]); const moveTypes = UTILS.javascript.getTreeValue(this.props.game.ordersTree, moveOrderPath); if (moveTypes !== null) { if (moveTypes.length === 2 && this.props.onSelectVia) { /* This move can be done either regularly or VIA a fleet. Let user choose. */ return this.props.onSelectVia(validLocations[0], orderBuilding.power, orderBuilding.path); } else { orderBuildingType = moveTypes[0]; } } } extendOrderBuilding( orderBuilding.power, orderBuildingType, orderBuilding.path, validLocations[0], this.props.onOrderBuilding, this.props.onOrderBuilt, this.props.onError ); } handleHoverID(id) { if (this.props.onHover) { const province = this.props.mapData.getProvince(id); if (province) { this.props.onHover(province.name, this.getRelatedOrders(province.name)); } } } getRelatedOrders(name) { const orders = []; if (this.props.orders) { for (let powerOrders of Object.values(this.props.orders)) { if (powerOrders) { for (let order of powerOrders) { const pieces = order.split(/ +/); if (pieces[1].slice(0, 3) === name.toUpperCase().slice(0, 3)) orders.push(order); } } } } return orders; } getNeighbors(extraLocation) { const selectedPath = [this.props.orderBuilding.type].concat(this.props.orderBuilding.path); if (extraLocation) selectedPath.push(extraLocation); const possibleNeighbors = UTILS.javascript.getTreeValue(this.props.game.ordersTree, selectedPath); const neighbors = possibleNeighbors ? possibleNeighbors.map(neighbor => parseLocation(neighbor)) : []; return neighbors.length ? neighbors: null; } render() { const classes = {"_vie":"nopower","_lon":"nopower","_par":"nopower","_ber":"nopower","_rom":"nopower","_mos":"nopower","_con":"nopower","CurrentNote":"currentnotetext","CurrentNote2":"currentnotetext","CurrentPhase":"currentphasetext","BriefLabelLayer":"labeltext","FullLabelLayer":"labeltext","MouseLayer":"invisibleContent"}; const game = this.props.game; const mapData = this.props.mapData; const orders = this.props.orders; /* Current phase. */ const current_phase = (game.phase[0] === '?' || game.phase === 'COMPLETED') ? 'FINAL' : game.phase; /* Notes. */ const nb_centers = []; for (let power of Object.values(game.powers)) { if (!power.isEliminated()) nb_centers.push([power.name.substr(0, 3), power.centers.length]); } /* Sort nb_centers by descending number of centers. */ nb_centers.sort((a, b) => { return -(a[1] - b[1]) || a[0].localeCompare(b[0]); }); const nb_centers_per_power = nb_centers.map((couple) => (couple[0] + ': ' + couple[1])).join(' '); const note = game.note; /* Adding units, influence and orders. */ const renderedUnits = []; const renderedDislodgedUnits = []; const renderedOrders = []; const renderedOrders2 = []; const renderedHighestOrders = []; for (let power of Object.values(game.powers)) if (!power.isEliminated()) { for (let unit of power.units) { renderedUnits.push( ); } for (let unit of Object.keys(power.retreats)) { renderedDislodgedUnits.push( ); } for (let center of power.centers) { setInfluence(classes, mapData, center, power.name); } for (let loc of power.influence) { if (!mapData.supplyCenters.has(loc)) setInfluence(classes, mapData, loc, power.name); } if (orders) { const powerOrders = (orders && orders.hasOwnProperty(power.name) && orders[power.name]) || []; for (let order of powerOrders) { const tokens = order.split(/ +/); if (!tokens || tokens.length < 3) continue; const unit_loc = tokens[1]; if (tokens[2] === 'H') { renderedOrders.push( ); } else if (tokens[2] === '-') { const destLoc = tokens[tokens.length - (tokens[tokens.length - 1] === 'VIA' ? 2 : 1)]; renderedOrders.push( ); } else if (tokens[2] === 'S') { const destLoc = tokens[tokens.length - 1]; if (tokens.includes('-')) { const srcLoc = tokens[4]; renderedOrders2.push( ); } else { renderedOrders2.push( ); } } else if (tokens[2] === 'C') { const srcLoc = tokens[4]; const destLoc = tokens[tokens.length - 1]; if ((srcLoc !== destLoc) && (tokens.includes('-'))) { renderedOrders2.push( ); } } else if (tokens[2] === 'B') { renderedHighestOrders.push( ); } else if (tokens[2] === 'D') { renderedHighestOrders.push( ); } else if (tokens[2] === 'R') { const destLoc = tokens[3]; renderedOrders.push( ); } else { throw new Error(`Unknown error to render (${order}).`); } } } } if (this.props.orderBuilding && this.props.orderBuilding.path.length) { const clicked = parseLocation(this.props.orderBuilding.path[0]); const province = this.props.mapData.getProvince(clicked); if (!province) throw new Error(('Unknown clicked province ' + clicked)); const clickedID = province.getID(classes); if (!clicked) throw new Error(`Unknown path (${clickedID}) for province (${clicked}).`); classes[clickedID] = 'provinceRed'; const neighbors = this.getNeighbors(); if (neighbors) { for (let neighbor of neighbors) { const neighborProvince = this.props.mapData.getProvince(neighbor); if (!neighborProvince) throw new Error('Unknown neighbor province ' + neighbor); const neighborID = neighborProvince.getID(classes); if (!neighborID) throw new Error(`Unknown neoghbor path (${neighborID}) for province (${neighbor}).`); classes[neighborID] = neighborProvince.isWater() ? 'provinceBlue' : 'provinceGreen'; } } } if (this.props.showAbbreviations === false) { classes['BriefLabelLayer'] = 'visibilityHidden'; } return ( Pure Paris London Vienna Berlin Constantinople Moscow Rome {nb_centers_per_power ? nb_centers_per_power : ''} {note ? note : ''} {current_phase} {renderedOrders2} {renderedOrders} {renderedUnits} {renderedDislodgedUnits} {renderedHighestOrders} ); } } SvgPure.propTypes = { game: PropTypes.instanceOf(Game).isRequired, mapData: PropTypes.instanceOf(MapData).isRequired, orders: PropTypes.object, onHover: PropTypes.func, onError: PropTypes.func.isRequired, onSelectLocation: PropTypes.func, onSelectVia: PropTypes.func, onOrderBuilding: PropTypes.func, onOrderBuilt: PropTypes.func, orderBuilding: PropTypes.object, showAbbreviations: PropTypes.bool }; // eslint-disable-line semi \ No newline at end of file diff --git a/diplomacy/web/src/gui/maps/pure/SvgPureMetadata.js b/diplomacy/web/src/gui/maps/pure/SvgPureMetadata.js new file mode 100644 index 0000000..51b5594 --- /dev/null +++ b/diplomacy/web/src/gui/maps/pure/SvgPureMetadata.js @@ -0,0 +1,21 @@ +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/ +export const Coordinates = {"BER":{"unit":["362.5","864.0"],"disl":["374.5","852.0"]},"CON":{"unit":["412.5","98.0"],"disl":["424.5","86.0"]},"LON":{"unit":["878.5","514.0"],"disl":["890.5","502.0"]},"MOS":{"unit":["140.5","289.0"],"disl":["152.5","277.0"]},"PAR":{"unit":["707.5","815.0"],"disl":["719.5","803.0"]},"ROM":{"unit":["116.5","628.0"],"disl":["128.5","616.0"]},"VIE":{"unit":["750.5","195.0"],"disl":["762.5","183.0"]}}; +export const SymbolSizes = {"Fleet":{"width":40.0,"height":40.0},"Army":{"width":40.0,"height":40.0},"Wing":{"width":40.0,"height":40.0},"DislodgedFleet":{"width":40.0,"height":40.0},"DislodgedArmy":{"width":40.0,"height":40.0},"DislodgedWing":{"width":40.0,"height":40.0},"FailedOrder":{"width":30.0,"height":30.0},"SupplyCenter":{"width":20.0,"height":20.0},"BuildUnit":{"width":60.0,"height":60.0},"RemoveUnit":{"width":50.0,"height":50.0},"WaivedBuild":{"width":40.0,"height":40.0},"HoldUnit":{"width":66.6,"height":66.6},"SupportHoldUnit":{"width":76.6,"height":76.6},"ConvoyTriangle":{"width":66.4,"height":57.4},"Stroke":{"width":6.0,"height":10.0}}; +export const Colors = {"AUSTRIA":"#DA251D","ENGLAND":"#2D77B2","FRANCE":"#8E85B7","GERMANY":"#666666","ITALY":"#40A340","RUSSIA":"#EEF0E9","TURKEY":"#E9F507"}; diff --git a/diplomacy/web/src/gui/maps/standard/SvgStandard.css b/diplomacy/web/src/gui/maps/standard/SvgStandard.css index 3aeebaa..f1da6c3 100644 --- a/diplomacy/web/src/gui/maps/standard/SvgStandard.css +++ b/diplomacy/web/src/gui/maps/standard/SvgStandard.css @@ -1,106 +1,19 @@ -/* text */ - svg { font-size: 100% } - .titletext {text-anchor:middle; stroke-width:0.3; font-family:sans-serif; font-size:0.7em; stroke:black; fill:black;} - .provtext {text-anchor:middle; stroke-width:0.3; font-family:sans-serif; font-size:0.7em; stroke:black; fill:black;} - .labeltext {stroke-width:0.1; stroke:black; fill:black;} - .unordered {fill:red; stroke:black; stroke-width:1; fill-opacity:0.90;} - .labeltext24 {text-anchor:middle; stroke-width:0.1; stroke:black; fill:black; font-family:serif,sans-serif; font-style:italic; font-size:1.4em;} - .labeltext18 {text-anchor:middle; stroke-width:0.1; stroke:black; fill:black; font-family:serif,sans-serif; font-style:italic; font-size:1.1em;} - .fulllabeltext {font-family:serif,sans-serif; font-style:italic; font-size:1.2em; fill:black; stroke:black;} - .currentnotetext {font-family:serif,sans-serif; font-size:1.5em; fill:black; stroke:black;} - .currentnoterect {fill:#c5dfea;} - .currentphasetext {font-family:serif,sans-serif; font-size:2.5em; fill:black; stroke:black;} - - .labeltext24 text {cursor:default;} - - /* NB: this style is not yet used. */ - .labeltext24 text.allowed { - font-weight:bold; - fill:blue; - font-size: 1.5em; - stroke: red; - stroke-width:2; - text-decoration: underline; - background-color: green; - } - - /* map and object features */ - - .seapoly {stroke:#000000; stroke-width:1; fill:#B5DEF8} - .dashline {stroke:darkslateblue; stroke-width:3; stroke-linecap:round; stroke-dasharray:5,6;} - .impassable {fill:#353433; stroke:#000000; stroke-width:1} - .sealine {stroke:#B5DEFF; stroke-width:3;} - - /* invisible click rects fill:none does not work */ - - .invisibleContent {stroke:#000000; fill:#000000; fill-opacity:0.0; opacity:0.0} - - /* default region coloring, by power */ - - .provinceRed {fill:url(#patternRed)} - .provinceBrown {fill:url(#patternBrown)} - .provinceGreen {fill:url(#patternGreen)} - .provinceBlack {fill:url(#patternBlack)} - .provinceBlue {fill:url(#patternBlue)} - - .nopower {fill:antiquewhite; stroke:#000000; stroke-width:1} - .water {fill:#c5dfea; stroke:#000000; stroke-width:1} - - .neutral {fill:lightgray; stroke:#000000; stroke-width:1} - - .austria {fill:#c48f85; stroke:#000000; stroke-width:1} - .england {fill:darkviolet; stroke:#000000; stroke-width:1} - .france {fill:royalblue; stroke:#000000; stroke-width:1} - .germany {fill:#a08a75; stroke:#000000; stroke-width:1} - .italy {fill:forestgreen; stroke:#000000; stroke-width:1} - .russia {fill:#757d91; stroke:#000000; stroke-width:1} - .turkey {fill:#b9a61c; stroke:#000000; stroke-width:1} - - /* unit colors, by power note that underscores are not supported */ - - .unitaustria {fill:red; fill-opacity:0.85} - .unitengland {fill:mediumpurple; fill-opacity:0.85} - .unitfrance {fill:deepskyblue; fill-opacity:0.85} - .unitgermany {fill:dimgray; fill-opacity:0.85} - .unititaly {fill:olive; fill-opacity:0.85} - .unitrussia {fill:white; fill-opacity:1.0} - .unitturkey {fill:yellow; fill-opacity:0.85} - - /* supply center styles */ - - .scnopower {fill:black; stroke:black;} - .scaustria {fill:black; stroke:black;} - .scengland {fill:black; stroke:black;} - .scfrance {fill:black; stroke:black;} - .scgermany {fill:black; stroke:black;} - .scitaly {fill:black; stroke:black;} - .scrussia {fill:black; stroke:black;} - .scturkey {fill:black; stroke:black;} - - /* order drawing styles, stroke and fill colors should not be specified */ - - .defaultorder {stroke-width:6; fill:none;} - .supportorder {stroke-width:6; fill:none; stroke-dasharray:5,5;} - .convoyorder {stroke-dasharray:15,5; stroke-width:6; fill:none;} - - .shadoworder {stroke-width:10; fill:none; stroke:black;} - .shadowdash {stroke-width:10; fill:none; stroke:black; opacity:0.45;} - - .varwidthorder {fill:none;} - .varwidthshadow {fill:none; stroke:black;} - - /* Symbol private styles. Always start with "sym" to avoid name collisions! */ - - .symBuildShadow {fill:none;stroke:black;opacity:0.5;stroke-width:7;} - .symBuild {stroke:yellow;stroke-width:7;fill:none;} - .symRemove {stroke:red;stroke-width:1;fill:none;} - - .symShadow {stroke:black;fill:black;stroke-width:1;opacity:0.40;} - .symDislodgedShadow {stroke:red;fill:red;stroke-width:1;opacity:0.50;} - .symDislodgedBorder {stroke:red;stroke-width:3%;} - - .symDarkener {fill:black;opacity:0.45;fill-opacity:0.45;} - .symCenterHub {fill:black; stroke:black; opacity:0.60; stroke-width:0.5px;} - .symBorder {stroke:black;stroke-width:3%;} - .symThinBorder {stroke:black;stroke-width:0.4;} - .symSilhouette {stroke:black;fill:black;stroke-width:1;} +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/ +/* text */ .SvgStandard svg { font-size: 100% } .SvgStandard .labeltext24 {text-anchor:middle; stroke-width:0.1; stroke:black; fill:black; font-family:serif,sans-serif; font-style:italic; font-size:1.4em;} .SvgStandard .labeltext18 {text-anchor:middle; stroke-width:0.1; stroke:black; fill:black; font-family:serif,sans-serif; font-style:italic; font-size:1.1em;} .SvgStandard .currentnotetext {font-family:serif,sans-serif; font-size:1.5em; fill:black; stroke:black;} .SvgStandard .currentnoterect {fill:#c5dfea;} .SvgStandard .currentphasetext {font-family:serif,sans-serif; font-size:2.5em; fill:black; stroke:black;} .SvgStandard .labeltext24 text {cursor:default;} /* map and object features */ .SvgStandard .impassable {fill:#353433; stroke:#000000; stroke-width:1} /* invisible click rects fill:none does not work */ .SvgStandard .invisibleContent {stroke:#000000; fill:#000000; fill-opacity:0.0; opacity:0.0} /* default region coloring, by power */ .SvgStandard .provinceRed {fill:url(#patternRed)} .SvgStandard .provinceBrown {fill:url(#patternBrown)} .SvgStandard .provinceGreen {fill:url(#patternGreen)} .SvgStandard .provinceBlack {fill:url(#patternBlack)} .SvgStandard .provinceBlue {fill:url(#patternBlue)} .SvgStandard .nopower {fill:antiquewhite; stroke:#000000; stroke-width:1} .SvgStandard .water {fill:#c5dfea; stroke:#000000; stroke-width:1} .SvgStandard .neutral {fill:lightgray; stroke:#000000; stroke-width:1} .SvgStandard .austria {fill:#c48f85; stroke:#000000; stroke-width:1} .SvgStandard .england {fill:darkviolet; stroke:#000000; stroke-width:1} .SvgStandard .france {fill:royalblue; stroke:#000000; stroke-width:1} .SvgStandard .germany {fill:#a08a75; stroke:#000000; stroke-width:1} .SvgStandard .italy {fill:forestgreen; stroke:#000000; stroke-width:1} .SvgStandard .russia {fill:#757d91; stroke:#000000; stroke-width:1} .SvgStandard .turkey {fill:#b9a61c; stroke:#000000; stroke-width:1} /* unit colors, by power note that underscores are not supported */ .SvgStandard .unitaustria {fill:red; fill-opacity:0.85} .SvgStandard .unitengland {fill:mediumpurple; fill-opacity:0.85} .SvgStandard .unitfrance {fill:deepskyblue; fill-opacity:0.85} .SvgStandard .unitgermany {fill:dimgray; fill-opacity:0.85} .SvgStandard .unititaly {fill:olive; fill-opacity:0.85} .SvgStandard .unitrussia {fill:white; fill-opacity:1.0} .SvgStandard .unitturkey {fill:yellow; fill-opacity:0.85} /* order drawing styles, stroke and fill colors should not be specified */ .SvgStandard .supportorder {stroke-width:6; fill:none; stroke-dasharray:5,5;} .SvgStandard .convoyorder {stroke-dasharray:15,5; stroke-width:6; fill:none;} .SvgStandard .shadowdash {stroke-width:10; fill:none; stroke:black; opacity:0.45;} .SvgStandard .varwidthorder {fill:none;} .SvgStandard .varwidthshadow {fill:none; stroke:black;} \ No newline at end of file diff --git a/diplomacy/web/src/gui/maps/standard/SvgStandard.js b/diplomacy/web/src/gui/maps/standard/SvgStandard.js index 87c6217..eb4104e 100644 --- a/diplomacy/web/src/gui/maps/standard/SvgStandard.js +++ b/diplomacy/web/src/gui/maps/standard/SvgStandard.js @@ -1,743 +1,20 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -/** Generated using svg_to_react.py with parameters: -Namespace(actionable=['MouseLayer'], input='src/diplomacy/maps/svg/standard.svg', name='SvgStandard', output='src/gui/maps/', remove=None) +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== **/ -import React from 'react'; -import PropTypes from 'prop-types'; -import "./SvgStandard.css"; -import {Game} from "../../../diplomacy/engine/game"; -import {MapData} from "../../utils/map_data"; -import {Unit} from "./unit"; -import {SupplyCenter} from "./supplyCenter"; -import {Hold} from "./hold"; -import {Move} from "./move"; -import {SupportMove} from "./supportMove"; -import {SupportHold} from "./supportHold"; -import {Convoy} from "./convoy"; -import {Build} from "./build"; -import {Disband} from "./disband"; -import {UTILS} from "../../../diplomacy/utils/utils"; -import {Diplog} from "../../../diplomacy/utils/diplog"; -import {extendOrderBuilding} from "../../utils/order_building"; - -function setInfluence(classes, mapData, loc, power_name) { - loc = loc.toUpperCase().substr(0, 3); - if (!['LAND', 'COAST'].includes(mapData.getProvince(loc).type)) - return; - const id = '_' + loc.toLowerCase(); - if (!classes.hasOwnProperty(id)) - throw new Error(`Unable to find SVG path for loc ${id}`); - classes[id] = power_name ? power_name.toLowerCase() : 'nopower'; -} - -function getClickedID(event) { - let node = event.target; - if (!node.id && node.parentNode.id && node.parentNode.tagName === 'g') - node = node.parentNode; - let id = node.id; - return id ? id.substr(0, 3) : null; -} - -function parseLocation(txt) { - if (txt.length > 2 && txt[1] === ' ' && ['A', 'F'].includes(txt[0])) - return txt.substr(2); - return txt; -} - -export class SvgStandard extends React.Component { - constructor(props) { - super(props); - this.onClick = this.onClick.bind(this); - this.onHover = this.onHover.bind(this); - } - onClick(event) { - if (this.props.orderBuilding) - return this.handleClickedID(getClickedID(event)); - } - onHover(event) { - return this.handleHoverID(getClickedID(event)); - } - handleClickedID(id) { - const orderBuilding = this.props.orderBuilding; - if (!orderBuilding.builder) - return this.props.onError('No orderable locations.'); - const province = this.props.mapData.getProvince(id); - if (!province) - return; - - const stepLength = orderBuilding.builder.steps.length; - if (orderBuilding.path.length >= stepLength) - throw new Error(`Order building: current steps count (${orderBuilding.path.length}) should be less than` + - ` expected steps count (${stepLength}) (${orderBuilding.path.join(', ')}).`); - - const lengthAfterClick = orderBuilding.path.length + 1; - let validLocations = []; - const testedPath = [orderBuilding.type].concat(orderBuilding.path); - const value = UTILS.javascript.getTreeValue(this.props.game.ordersTree, testedPath); - if (value !== null) { - const checker = orderBuilding.builder.steps[lengthAfterClick - 1]; - try { - const possibleLocations = checker(province, orderBuilding.power); - for (let possibleLocation of possibleLocations) { - possibleLocation = possibleLocation.toUpperCase(); - if (value.includes(possibleLocation)) - validLocations.push(possibleLocation); - } - } catch (error) { - return this.props.onError(error); - } - } - if (!validLocations.length) - return this.props.onError('Disallowed.'); - - if (validLocations.length > 1 && orderBuilding.type === 'S' && orderBuilding.path.length >= 2) { - // We are building a support order and we have a multiple choice for a location. - // Let's check if next location to choose is a coast. To have a coast: - // - all possible locations must start with same 3 characters. - // - we expect at least province name in possible locations (e.g. 'SPA' for 'SPA/NC'). - // If we have a coast, we will remove province name from possible locations. - let isACoast = true; - let validLocationsNoProvinceName = []; - for (let i = 0; i < validLocations.length; ++i) { - let location = validLocations[i]; - if (i > 0) { - // Compare 3 first letters with previous location. - if (validLocations[i - 1].substring(0, 3).toUpperCase() !== validLocations[i].substring(0, 3).toUpperCase()) { - // No same prefix with previous location. We does not have a coast. - isACoast = false; - break; - } - } - if (location.length !== 3) - validLocationsNoProvinceName.push(location); - } - if (validLocations.length === validLocationsNoProvinceName.length) { - // We have not found province name. - isACoast = false; - } - if (isACoast) { - // We want to choose location in a coastal province. Let's remove province name. - validLocations = validLocationsNoProvinceName; - } - } - - if (validLocations.length > 1) { - if (this.props.onSelectLocation) { - return this.props.onSelectLocation(validLocations, orderBuilding.power, orderBuilding.type, orderBuilding.path); - } else { - Diplog.warn(`Forced to select first valid location.`); - validLocations = [validLocations[0]]; - } - } - let orderBuildingType = orderBuilding.type; - if (lengthAfterClick === stepLength && orderBuildingType === 'M') { - const moveOrderPath = ['M'].concat(orderBuilding.path, validLocations[0]); - const moveTypes = UTILS.javascript.getTreeValue(this.props.game.ordersTree, moveOrderPath); - if (moveTypes !== null) { - if (moveTypes.length === 2 && this.props.onSelectVia) { - // This move can be done either regularly or VIA a fleet. Let user choose. - return this.props.onSelectVia(validLocations[0], orderBuilding.power, orderBuilding.path); - } else { - orderBuildingType = moveTypes[0]; - } - } - } - extendOrderBuilding( - orderBuilding.power, orderBuildingType, orderBuilding.path, validLocations[0], - this.props.onOrderBuilding, this.props.onOrderBuilt, this.props.onError - ); - } - handleHoverID(id) { - if (this.props.onHover) { - const province = this.props.mapData.getProvince(id); - if (province) { - this.props.onHover(province.name, this.getRelatedOrders(province.name)); - } - } - } - getRelatedOrders(name) { - const orders = []; - if (this.props.orders) { - for (let powerOrders of Object.values(this.props.orders)) { - if (powerOrders) { - for (let order of powerOrders) { - const pieces = order.split(/ +/); - if (pieces[1].slice(0, 3) === name.toUpperCase().slice(0, 3)) - orders.push(order); - } - } - } - } - return orders; - } - getNeighbors(extraLocation) { - const selectedPath = [this.props.orderBuilding.type].concat(this.props.orderBuilding.path); - if (extraLocation) - selectedPath.push(extraLocation); - const possibleNeighbors = UTILS.javascript.getTreeValue(this.props.game.ordersTree, selectedPath); - const neighbors = possibleNeighbors ? possibleNeighbors.map(neighbor => parseLocation(neighbor)) : []; - return neighbors.length ? neighbors: null; - } - render() { - const classes = {"_ank":"nopower","_arm":"nopower","_con":"nopower","_mos":"nopower","_sev":"nopower","_stp":"nopower","_syr":"nopower","_ukr":"nopower","_lvn":"nopower","_war":"nopower","_pru":"nopower","_sil":"nopower","_ber":"nopower","_kie":"nopower","_ruh":"nopower","_mun":"nopower","_rum":"nopower","_bul":"nopower","_gre":"nopower","_smy":"nopower","_alb":"nopower","_ser":"nopower","_bud":"nopower","_gal":"nopower","_vie":"nopower","_boh":"nopower","_tyr":"nopower","_tri":"nopower","_fin":"nopower","_swe":"nopower","_nwy":"nopower","_den":"nopower","_hol":"nopower","_bel":"nopower","_swi":"impassable","_ven":"nopower","_pie":"nopower","_tus":"nopower","_rom":"nopower","_apu":"nopower","_nap":"nopower","_bur":"nopower","_mar":"nopower","_gas":"nopower","_pic":"nopower","_par":"nopower","_bre":"nopower","_spa":"nopower","_por":"nopower","_naf":"nopower","_tun":"nopower","_lon":"nopower","_wal":"nopower","_lvp":"nopower","_yor":"nopower","_edi":"nopower","_cly":"nopower","unplayable":"neutral","unplayable_water":"water","_nat":"water","_nrg":"water","_bar":"water","_bot":"water","_bal":"water","denmark_water":"water","_ska":"water","_hel":"water","_nth":"water","_eng":"water","_iri":"water","_mid":"water","_wes":"water","_gol":"water","_tyn":"water","_adr":"water","_ion":"water","_aeg":"water","_eas":"water","constantinople_water":"water","_bla":"water","BriefLabelLayer":"labeltext24","CurrentNote":"currentnotetext","CurrentNote2":"currentnotetext","CurrentPhase":"currentphasetext","MouseLayer":"invisibleContent"}; - const game = this.props.game; - const mapData = this.props.mapData; - const orders = this.props.orders; - - //// Current phase. - const current_phase = (game.phase[0] === '?' || game.phase === 'COMPLETED') ? 'FINAL' : game.phase; - - //// Notes. - const nb_centers = []; - for (let power of Object.values(game.powers)) { - if (!power.isEliminated()) - nb_centers.push([power.name.substr(0, 3), power.centers.length]); - } - // Sort nb_centers by descending number of centers. - nb_centers.sort((a, b) => { - return -(a[1] - b[1]) || a[0].localeCompare(b[0]); - }); - const nb_centers_per_power = nb_centers.map((couple) => (couple[0] + ': ' + couple[1])).join(' '); - const note = game.note; - - //// Adding units, supply centers, influence and orders. - const scs = new Set(mapData.supplyCenters); - const renderedUnits = []; - const renderedDislodgedUnits = []; - const renderedSupplyCenters = []; - const renderedOrders = []; - const renderedOrders2 = []; - const renderedHighestOrders = []; - for (let power of Object.values(game.powers)) { - for (let unit of power.units) { - renderedUnits.push(); - } - for (let unit of Object.keys(power.retreats)) { - renderedDislodgedUnits.push(); - } - for (let center of power.centers) { - renderedSupplyCenters.push(); - setInfluence(classes, mapData, center, power.name); - scs.delete(center); - } - if (!power.isEliminated()) { - for (let loc of power.influence) { - if (!mapData.supplyCenters.has(loc)) - setInfluence(classes, mapData, loc, power.name); - } - } - - if (orders) { - const powerOrders = (orders && orders.hasOwnProperty(power.name) && orders[power.name]) || []; - for (let order of powerOrders) { - const tokens = order.split(/ +/); - if (!tokens || tokens.length < 3) - continue; - const unit_loc = tokens[1]; - if (tokens[2] === 'H') { - renderedOrders.push(); - } else if (tokens[2] === '-') { - const destLoc = tokens[tokens.length - (tokens[tokens.length - 1] === 'VIA' ? 2 : 1)]; - renderedOrders.push(); - } else if (tokens[2] === 'S') { - const destLoc = tokens[tokens.length - 1]; - if (tokens.includes('-')) { - const srcLoc = tokens[4]; - renderedOrders2.push(); - } else { - renderedOrders2.push(); - } - } else if (tokens[2] === 'C') { - const srcLoc = tokens[4]; - const destLoc = tokens[tokens.length - 1]; - if ((srcLoc !== destLoc) && (tokens.includes('-'))) { - renderedOrders2.push(); - } - } else if (tokens[2] === 'B') { - renderedHighestOrders.push(); - } else if (tokens[2] === 'D') { - renderedHighestOrders.push(); - } else if (tokens[2] === 'R') { - const srcLoc = tokens[1]; - const destLoc = tokens[3]; - renderedOrders.push(); - } else { - throw new Error(`Unknown error to render (${order}).`); - } - } - } - } - // Adding remaining supply centers. - for (let remainingCenter of scs) { - renderedSupplyCenters.push(); - } - - if (this.props.orderBuilding && this.props.orderBuilding.path.length) { - const clicked = parseLocation(this.props.orderBuilding.path[0]); - const province = this.props.mapData.getProvince(clicked); - if (!province) - throw new Error(('Unknown clicked province ' + clicked)); - const clickedID = province.getID(classes); - if (!clicked) - throw new Error(`Unknown path (${clickedID}) for province (${clicked}).`); - classes[clickedID] = 'provinceRed'; - const neighbors = this.getNeighbors(); - if (neighbors) { - for (let neighbor of neighbors) { - const neighborProvince = this.props.mapData.getProvince(neighbor); - if (!neighborProvince) - throw new Error('Unknown neighbor province ' + neighbor); - const neighborID = neighborProvince.getID(classes); - if (!neighborID) - throw new Error(`Unknown neoghbor path (${neighborID}) for province (${neighbor}).`); - classes[neighborID] = neighborProvince.isWater() ? 'provinceBlue' : 'provinceGreen'; - } - } - } - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {renderedSupplyCenters} - - - - {renderedOrders2} - - - {renderedOrders} - - - - {renderedUnits} - - - {renderedDislodgedUnits} - - - {renderedHighestOrders} - - - SWI - ADR - AEG - ALB - ANK - APU - ARM - BAL - BAR - BEL - BER - BLA - BOH - BRE - BUD - BUL - BUR - CLY - CON - DEN - EAS - EDI - ENG - FIN - GAL - GAS - GRE - BOT - LYO - HEL - HOL - ION - IRI - KIE - LON - LVN - LVP - MAR - MAO - MOS - MUN - NAF - NAP - NAO - NTH - NWY - NWG - PAR - PIC - PIE - POR - PRU - ROM - RUH - RUM - SER - SEV - SIL - SKA - SMY - SPA - STP - SWE - SYR - TRI - TUN - TUS - TYR - TYS - UKR - VEN - VIE - WAL - WAR - WES - YOR - - - - {nb_centers_per_power ? nb_centers_per_power : ''} - - - {note ? note : ''} - - - {current_phase} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); - } -} -SvgStandard.propTypes = { - game: PropTypes.instanceOf(Game).isRequired, - mapData: PropTypes.instanceOf(MapData).isRequired, - orders: PropTypes.object, - onHover: PropTypes.func, - onError: PropTypes.func.isRequired, - onSelectLocation: PropTypes.func, - onSelectVia: PropTypes.func, - onOrderBuilding: PropTypes.func, - onOrderBuilt: PropTypes.func, - orderBuilding: PropTypes.object -}; +/** Generated with parameters: Namespace(input='src/diplomacy/maps/svg/standard.svg', name='SvgStandard', output='src/gui/maps/standard/') **/ + import React from 'react'; import PropTypes from 'prop-types'; import './SvgStandard.css'; import {Coordinates, SymbolSizes, Colors} from "./SvgStandardMetadata"; import {getClickedID, parseLocation, setInfluence} from "../common/common"; import {Game} from "../../../diplomacy/engine/game"; import {MapData} from "../../utils/map_data"; import {UTILS} from "../../../diplomacy/utils/utils"; import {Diplog} from "../../../diplomacy/utils/diplog"; import {extendOrderBuilding} from "../../utils/order_building"; import {Unit} from "../common/unit"; import {Hold} from "../common/hold"; import {Move} from "../common/move"; import {SupportMove} from "../common/supportMove"; import {SupportHold} from "../common/supportHold"; import {Convoy} from "../common/convoy"; import {Build} from "../common/build"; import {Disband} from "../common/disband"; export class SvgStandard extends React.Component { constructor(props) { super(props); this.onClick = this.onClick.bind(this); this.onHover = this.onHover.bind(this); } onClick(event) { if (this.props.orderBuilding) return this.handleClickedID(getClickedID(event)); } onHover(event) { return this.handleHoverID(getClickedID(event)); } handleClickedID(id) { const orderBuilding = this.props.orderBuilding; if (!orderBuilding.builder) return this.props.onError('No orderable locations.'); const province = this.props.mapData.getProvince(id); if (!province) throw new Error(`Cannot find a province named ${id}`); const stepLength = orderBuilding.builder.steps.length; if (orderBuilding.path.length >= stepLength) throw new Error(`Order building: current steps count (${orderBuilding.path.length}) should be less than expected steps count (${stepLength}) (${orderBuilding.path.join(', ')}).`); const lengthAfterClick = orderBuilding.path.length + 1; let validLocations = []; const testedPath = [orderBuilding.type].concat(orderBuilding.path); const value = UTILS.javascript.getTreeValue(this.props.game.ordersTree, testedPath); if (value !== null) { const checker = orderBuilding.builder.steps[lengthAfterClick - 1]; try { const possibleLocations = checker(province, orderBuilding.power); for (let possibleLocation of possibleLocations) { possibleLocation = possibleLocation.toUpperCase(); if (value.includes(possibleLocation)) validLocations.push(possibleLocation); } } catch (error) { return this.props.onError(error); } } if (!validLocations.length) return this.props.onError('Disallowed.'); if (validLocations.length > 1 && orderBuilding.type === 'S' && orderBuilding.path.length >= 2) { /* We are building a support order and we have a multiple choice for a location. */ /* Let's check if next location to choose is a coast. To have a coast: */ /* - all possible locations must start with same 3 characters. */ /* - we expect at least province name in possible locations (e.g. 'SPA' for 'SPA/NC'). */ /* If we have a coast, we will remove province name from possible locations. */ let isACoast = true; let validLocationsNoProvinceName = []; for (let i = 0; i < validLocations.length; ++i) { let location = validLocations[i]; if (i > 0) { /* Compare 3 first letters with previous location. */ if (validLocations[i - 1].substring(0, 3).toUpperCase() !== validLocations[i].substring(0, 3).toUpperCase()) { /* No same prefix with previous location. We does not have a coast. */ isACoast = false; break; } } if (location.length !== 3) validLocationsNoProvinceName.push(location); } if (validLocations.length === validLocationsNoProvinceName.length) { /* We have not found province name. */ isACoast = false; } if (isACoast) { /* We want to choose location in a coastal province. Let's remove province name. */ validLocations = validLocationsNoProvinceName; } } if (validLocations.length > 1) { if (this.props.onSelectLocation) { return this.props.onSelectLocation(validLocations, orderBuilding.power, orderBuilding.type, orderBuilding.path); } else { Diplog.warn(`Forced to select first valid location.`); validLocations = [validLocations[0]]; } } let orderBuildingType = orderBuilding.type; if (lengthAfterClick === stepLength && orderBuildingType === 'M') { const moveOrderPath = ['M'].concat(orderBuilding.path, validLocations[0]); const moveTypes = UTILS.javascript.getTreeValue(this.props.game.ordersTree, moveOrderPath); if (moveTypes !== null) { if (moveTypes.length === 2 && this.props.onSelectVia) { /* This move can be done either regularly or VIA a fleet. Let user choose. */ return this.props.onSelectVia(validLocations[0], orderBuilding.power, orderBuilding.path); } else { orderBuildingType = moveTypes[0]; } } } extendOrderBuilding( orderBuilding.power, orderBuildingType, orderBuilding.path, validLocations[0], this.props.onOrderBuilding, this.props.onOrderBuilt, this.props.onError ); } handleHoverID(id) { if (this.props.onHover) { const province = this.props.mapData.getProvince(id); if (province) { this.props.onHover(province.name, this.getRelatedOrders(province.name)); } } } getRelatedOrders(name) { const orders = []; if (this.props.orders) { for (let powerOrders of Object.values(this.props.orders)) { if (powerOrders) { for (let order of powerOrders) { const pieces = order.split(/ +/); if (pieces[1].slice(0, 3) === name.toUpperCase().slice(0, 3)) orders.push(order); } } } } return orders; } getNeighbors(extraLocation) { const selectedPath = [this.props.orderBuilding.type].concat(this.props.orderBuilding.path); if (extraLocation) selectedPath.push(extraLocation); const possibleNeighbors = UTILS.javascript.getTreeValue(this.props.game.ordersTree, selectedPath); const neighbors = possibleNeighbors ? possibleNeighbors.map(neighbor => parseLocation(neighbor)) : []; return neighbors.length ? neighbors: null; } render() { const classes = {"_ank":"nopower","_arm":"nopower","_con":"nopower","_mos":"nopower","_sev":"nopower","_stp":"nopower","_syr":"nopower","_ukr":"nopower","_lvn":"nopower","_war":"nopower","_pru":"nopower","_sil":"nopower","_ber":"nopower","_kie":"nopower","_ruh":"nopower","_mun":"nopower","_rum":"nopower","_bul":"nopower","_gre":"nopower","_smy":"nopower","_alb":"nopower","_ser":"nopower","_bud":"nopower","_gal":"nopower","_vie":"nopower","_boh":"nopower","_tyr":"nopower","_tri":"nopower","_fin":"nopower","_swe":"nopower","_nwy":"nopower","_den":"nopower","_hol":"nopower","_bel":"nopower","_swi":"impassable","_ven":"nopower","_pie":"nopower","_tus":"nopower","_rom":"nopower","_apu":"nopower","_nap":"nopower","_bur":"nopower","_mar":"nopower","_gas":"nopower","_pic":"nopower","_par":"nopower","_bre":"nopower","_spa":"nopower","_por":"nopower","_naf":"nopower","_tun":"nopower","_lon":"nopower","_wal":"nopower","_lvp":"nopower","_yor":"nopower","_edi":"nopower","_cly":"nopower","unplayable":"neutral","unplayable_water":"water","_nat":"water","_nrg":"water","_bar":"water","_bot":"water","_bal":"water","denmark_water":"water","_ska":"water","_hel":"water","_nth":"water","_eng":"water","_iri":"water","_mid":"water","_wes":"water","_gol":"water","_tyn":"water","_adr":"water","_ion":"water","_aeg":"water","_eas":"water","constantinople_water":"water","_bla":"water","BriefLabelLayer":"labeltext24","CurrentNote":"currentnotetext","CurrentNote2":"currentnotetext","CurrentPhase":"currentphasetext","MouseLayer":"invisibleContent"}; const game = this.props.game; const mapData = this.props.mapData; const orders = this.props.orders; /* Current phase. */ const current_phase = (game.phase[0] === '?' || game.phase === 'COMPLETED') ? 'FINAL' : game.phase; /* Notes. */ const nb_centers = []; for (let power of Object.values(game.powers)) { if (!power.isEliminated()) nb_centers.push([power.name.substr(0, 3), power.centers.length]); } /* Sort nb_centers by descending number of centers. */ nb_centers.sort((a, b) => { return -(a[1] - b[1]) || a[0].localeCompare(b[0]); }); const nb_centers_per_power = nb_centers.map((couple) => (couple[0] + ': ' + couple[1])).join(' '); const note = game.note; /* Adding units, influence and orders. */ const renderedUnits = []; const renderedDislodgedUnits = []; const renderedOrders = []; const renderedOrders2 = []; const renderedHighestOrders = []; for (let power of Object.values(game.powers)) if (!power.isEliminated()) { for (let unit of power.units) { renderedUnits.push( ); } for (let unit of Object.keys(power.retreats)) { renderedDislodgedUnits.push( ); } for (let center of power.centers) { setInfluence(classes, mapData, center, power.name); } for (let loc of power.influence) { if (!mapData.supplyCenters.has(loc)) setInfluence(classes, mapData, loc, power.name); } if (orders) { const powerOrders = (orders && orders.hasOwnProperty(power.name) && orders[power.name]) || []; for (let order of powerOrders) { const tokens = order.split(/ +/); if (!tokens || tokens.length < 3) continue; const unit_loc = tokens[1]; if (tokens[2] === 'H') { renderedOrders.push( ); } else if (tokens[2] === '-') { const destLoc = tokens[tokens.length - (tokens[tokens.length - 1] === 'VIA' ? 2 : 1)]; renderedOrders.push( ); } else if (tokens[2] === 'S') { const destLoc = tokens[tokens.length - 1]; if (tokens.includes('-')) { const srcLoc = tokens[4]; renderedOrders2.push( ); } else { renderedOrders2.push( ); } } else if (tokens[2] === 'C') { const srcLoc = tokens[4]; const destLoc = tokens[tokens.length - 1]; if ((srcLoc !== destLoc) && (tokens.includes('-'))) { renderedOrders2.push( ); } } else if (tokens[2] === 'B') { renderedHighestOrders.push( ); } else if (tokens[2] === 'D') { renderedHighestOrders.push( ); } else if (tokens[2] === 'R') { const destLoc = tokens[3]; renderedOrders.push( ); } else { throw new Error(`Unknown error to render (${order}).`); } } } } if (this.props.orderBuilding && this.props.orderBuilding.path.length) { const clicked = parseLocation(this.props.orderBuilding.path[0]); const province = this.props.mapData.getProvince(clicked); if (!province) throw new Error(('Unknown clicked province ' + clicked)); const clickedID = province.getID(classes); if (!clicked) throw new Error(`Unknown path (${clickedID}) for province (${clicked}).`); classes[clickedID] = 'provinceRed'; const neighbors = this.getNeighbors(); if (neighbors) { for (let neighbor of neighbors) { const neighborProvince = this.props.mapData.getProvince(neighbor); if (!neighborProvince) throw new Error('Unknown neighbor province ' + neighbor); const neighborID = neighborProvince.getID(classes); if (!neighborID) throw new Error(`Unknown neoghbor path (${neighborID}) for province (${neighbor}).`); classes[neighborID] = neighborProvince.isWater() ? 'provinceBlue' : 'provinceGreen'; } } } if (this.props.showAbbreviations === false) { classes['BriefLabelLayer'] = 'visibilityHidden'; } return ( {renderedOrders2} {renderedOrders} {renderedUnits} {renderedDislodgedUnits} {renderedHighestOrders} SWI ADR AEG ALB ANK APU ARM BAL BAR BEL BER BLA BOH BRE BUD BUL BUR CLY CON DEN EAS EDI ENG FIN GAL GAS GRE BOT LYO HEL HOL ION IRI KIE LON LVN LVP MAR MAO MOS MUN NAF NAP NAO NTH NWY NWG PAR PIC PIE POR PRU ROM RUH RUM SER SEV SIL SKA SMY SPA STP SWE SYR TRI TUN TUS TYR TYS UKR VEN VIE WAL WAR WES YOR {nb_centers_per_power ? nb_centers_per_power : ''} {note ? note : ''} {current_phase} ); } } SvgStandard.propTypes = { game: PropTypes.instanceOf(Game).isRequired, mapData: PropTypes.instanceOf(MapData).isRequired, orders: PropTypes.object, onHover: PropTypes.func, onError: PropTypes.func.isRequired, onSelectLocation: PropTypes.func, onSelectVia: PropTypes.func, onOrderBuilding: PropTypes.func, onOrderBuilt: PropTypes.func, orderBuilding: PropTypes.object, showAbbreviations: PropTypes.bool }; // eslint-disable-line semi \ No newline at end of file diff --git a/diplomacy/web/src/gui/maps/standard/SvgStandardExtra.js b/diplomacy/web/src/gui/maps/standard/SvgStandardExtra.js deleted file mode 100644 index be4e02e..0000000 --- a/diplomacy/web/src/gui/maps/standard/SvgStandardExtra.js +++ /dev/null @@ -1,1279 +0,0 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -export const SvgStandardExtra = { - "jdipNS:DISPLAY": { - "jdipNS:ZOOM": { - "min": "5", - "max": "2200", - "factor": "1.2" - }, - "jdipNS:LABELS": { - "brief": "true", - "full": "true" - } - }, - "jdipNS:ORDERDRAWING": { - "jdipNS:POWERCOLORS": { - "jdipNS:POWERCOLOR": [ - { - "power": "austria", - "color": "#c48f85" - }, - { - "power": "england", - "color": "darkviolet" - }, - { - "power": "france", - "color": "royalblue" - }, - { - "power": "germany", - "color": "#a08a75" - }, - { - "power": "italy", - "color": "forestgreen" - }, - { - "power": "russia", - "color": "#757d91" - }, - { - "power": "turkey", - "color": "#b9a61c" - } - ] - }, - "jdipNS:SYMBOLSIZE": [ - { - "name": "Fleet", - "width": "40", - "height": "40" - }, - { - "name": "Army", - "width": "40", - "height": "40" - }, - { - "name": "Wing", - "width": "40", - "height": "40" - }, - { - "name": "DislodgedFleet", - "width": "40", - "height": "40" - }, - { - "name": "DislodgedArmy", - "width": "40", - "height": "40" - }, - { - "name": "DislodgedWing", - "width": "40", - "height": "40" - }, - { - "name": "FailedOrder", - "width": "30", - "height": "30" - }, - { - "name": "SupplyCenter", - "width": "20", - "height": "20" - }, - { - "name": "BuildUnit", - "width": "60", - "height": "60" - }, - { - "name": "RemoveUnit", - "width": "50", - "height": "50" - }, - { - "name": "WaivedBuild", - "width": "40", - "height": "40" - } - ], - "jdipNS:BUILD": { - "deltaRadius": "0" - }, - "jdipNS:REMOVE": { - "deltaRadius": "5" - }, - "jdipNS:DISBAND": { - "deltaRadius": "5" - }, - "jdipNS:WAIVE": { - "deltaRadius": "0" - }, - "jdipNS:HOLD": { - "deltaRadius": "5", - "strokeCSSStyle": "varwidthorder", - "highlightOffset": "0", - "highlightCSSClass": "varwidthshadow", - "widths": "6,9,12,18", - "shadowWidths": "10,15,20,25" - }, - "jdipNS:MOVE": { - "deltaRadius": "5", - "strokeCSSStyle": "varwidthorder", - "markerID": "arrow", - "highlightOffset": "0", - "highlightCSSClass": "varwidthshadow", - "widths": "6,9,12,18", - "shadowWidths": "10,15,20,25" - }, - "jdipNS:RETREAT": { - "deltaRadius": "5", - "strokeCSSStyle": "defaultorder", - "markerID": "arrow", - "highlightOffset": "0", - "highlightCSSClass": "shadoworder" - }, - "jdipNS:SUPPORT": { - "deltaRadius": "10", - "strokeCSSStyle": "supportorder", - "markerID": "arrow", - "highlightOffset": "0", - "highlightCSSClass": "shadowdash" - }, - "jdipNS:CONVOY": { - "deltaRadius": "10", - "strokeCSSStyle": "convoyorder", - "markerID": "arrow", - "highlightOffset": "0", - "highlightCSSClass": "shadowdash" - } - }, - "jdipNS:PROVINCE_DATA": { - "jdipNS:PROVINCE": [ - { - "jdipNS:UNIT": { - "x": "805", - "y": "1058" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "792.5", - "y": "1045.5" - }, - "name": "adr" - }, - { - "jdipNS:UNIT": { - "x": "1055", - "y": "1240" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1042.5", - "y": "1227.5" - }, - "name": "aeg" - }, - { - "jdipNS:UNIT": { - "x": "918", - "y": "1123" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "905.5", - "y": "1110.5" - }, - "name": "alb" - }, - { - "jdipNS:UNIT": { - "x": "1313", - "y": "1120" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1300.5", - "y": "1107.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "1271", - "y": "1144" - }, - "name": "ank" - }, - { - "jdipNS:UNIT": { - "x": "803", - "y": "1116" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "790.5", - "y": "1103.5" - }, - "name": "apu" - }, - { - "jdipNS:UNIT": { - "x": "1496", - "y": "1100" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1483.5", - "y": "1087.5" - }, - "name": "arm" - }, - { - "jdipNS:UNIT": { - "x": "890", - "y": "620" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "877.5", - "y": "607.5" - }, - "name": "bal" - }, - { - "jdipNS:UNIT": { - "x": "1174", - "y": "83" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1161.5", - "y": "70.5" - }, - "name": "bar" - }, - { - "jdipNS:UNIT": { - "x": "573", - "y": "763" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "560.5", - "y": "750.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "575", - "y": "755" - }, - "name": "bel" - }, - { - "jdipNS:UNIT": { - "x": "783", - "y": "700" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "770.5", - "y": "687.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "783", - "y": "736" - }, - "name": "ber" - }, - { - "jdipNS:UNIT": { - "x": "1245", - "y": "1010" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1232.5", - "y": "997.5" - }, - "name": "bla" - }, - { - "jdipNS:UNIT": { - "x": "818", - "y": "824" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "805.5", - "y": "811.5" - }, - "name": "boh" - }, - { - "jdipNS:UNIT": { - "x": "953", - "y": "495" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "940.5", - "y": "482.5" - }, - "name": "bot" - }, - { - "jdipNS:UNIT": { - "x": "416", - "y": "829" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "403.5", - "y": "816.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "369", - "y": "799" - }, - "name": "bre" - }, - { - "jdipNS:UNIT": { - "x": "962", - "y": "914" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "949.5", - "y": "901.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "900", - "y": "916" - }, - "name": "bud" - }, - { - "jdipNS:UNIT": { - "x": "1060", - "y": "1078" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1047.5", - "y": "1065.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "1013", - "y": "1078" - }, - "name": "bul" - }, - { - "jdipNS:UNIT": { - "x": "571", - "y": "881" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "558.5", - "y": "868.5" - }, - "name": "bur" - }, - { - "jdipNS:UNIT": { - "x": "448", - "y": "502" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "435.5", - "y": "489.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "446", - "y": "480" - }, - "name": "cly" - }, - { - "jdipNS:UNIT": { - "x": "1157", - "y": "1147" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1144.5", - "y": "1134.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "1141", - "y": "1119" - }, - "name": "con" - }, - { - "jdipNS:UNIT": { - "x": "715", - "y": "597" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "702.5", - "y": "584.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "767", - "y": "626" - }, - "name": "den" - }, - { - "jdipNS:UNIT": { - "x": "1230", - "y": "1321" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1217.5", - "y": "1308.5" - }, - "name": "eas" - }, - { - "jdipNS:UNIT": { - "x": "485", - "y": "524" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "472.5", - "y": "511.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "489", - "y": "565" - }, - "name": "edi" - }, - { - "jdipNS:UNIT": { - "x": "406", - "y": "761" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "393.5", - "y": "748.5" - }, - "name": "eng" - }, - { - "jdipNS:UNIT": { - "x": "1000", - "y": "390" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "987.5", - "y": "377.5" - }, - "name": "fin" - }, - { - "jdipNS:UNIT": { - "x": "1011", - "y": "841" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "998.5", - "y": "828.5" - }, - "name": "gal" - }, - { - "jdipNS:UNIT": { - "x": "434", - "y": "922" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "421.5", - "y": "909.5" - }, - "name": "gas" - }, - { - "jdipNS:UNIT": { - "x": "556", - "y": "1060" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "543.5", - "y": "1047.5" - }, - "name": "gol" - }, - { - "jdipNS:UNIT": { - "x": "978", - "y": "1200" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "965.5", - "y": "1187.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "1023", - "y": "1237" - }, - "name": "gre" - }, - { - "jdipNS:UNIT": { - "x": "663", - "y": "641" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "650.5", - "y": "628.5" - }, - "name": "hel" - }, - { - "jdipNS:UNIT": { - "x": "608", - "y": "721" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "595.5", - "y": "708.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "630", - "y": "692" - }, - "name": "hol" - }, - { - "jdipNS:UNIT": { - "x": "858", - "y": "1296" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "845.5", - "y": "1283.5" - }, - "name": "ion" - }, - { - "jdipNS:UNIT": { - "x": "347", - "y": "671" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "334.5", - "y": "658.5" - }, - "name": "iri" - }, - { - "jdipNS:UNIT": { - "x": "695", - "y": "711" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "682.5", - "y": "698.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "724", - "y": "685" - }, - "name": "kie" - }, - { - "jdipNS:UNIT": { - "x": "500", - "y": "685" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "487.5", - "y": "672.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "489", - "y": "717" - }, - "name": "lon" - }, - { - "jdipNS:UNIT": { - "x": "1037", - "y": "577" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1024.5", - "y": "564.5" - }, - "name": "lvn" - }, - { - "jdipNS:UNIT": { - "x": "462", - "y": "586" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "449.5", - "y": "573.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "458", - "y": "638" - }, - "name": "lvp" - }, - { - "jdipNS:UNIT": { - "x": "525.8", - "y": "1065" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "513.3", - "y": "1052.5" - }, - "name": "lyo" - }, - { - "jdipNS:UNIT": { - "x": "536", - "y": "985" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "523.5", - "y": "972.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "562", - "y": "1018" - }, - "name": "mar" - }, - { - "jdipNS:UNIT": { - "x": "153.3", - "y": "845.3" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "140.8", - "y": "832.8" - }, - "name": "mao" - }, - { - "jdipNS:UNIT": { - "x": "126", - "y": "902" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "113.5", - "y": "889.5" - }, - "name": "mid" - }, - { - "jdipNS:UNIT": { - "x": "1212", - "y": "600" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1199.5", - "y": "587.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "1267", - "y": "584" - }, - "name": "mos" - }, - { - "jdipNS:UNIT": { - "x": "705", - "y": "838" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "692.5", - "y": "825.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "730", - "y": "878" - }, - "name": "mun" - }, - { - "jdipNS:UNIT": { - "x": "337", - "y": "1291" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "324.5", - "y": "1278.5" - }, - "name": "naf" - }, - { - "jdipNS:UNIT": { - "x": "191.6", - "y": "298.2" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "179.1", - "y": "285.7" - }, - "name": "nao" - }, - { - "jdipNS:UNIT": { - "x": "818", - "y": "1180" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "815.5", - "y": "1167.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "778", - "y": "1143" - }, - "name": "nap" - }, - { - "jdipNS:UNIT": { - "x": "238", - "y": "427" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "225.5", - "y": "414.5" - }, - "name": "nat" - }, - { - "jdipNS:UNIT": { - "x": "605", - "y": "250" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "592.5", - "y": "237.5" - }, - "name": "nrg" - }, - { - "jdipNS:UNIT": { - "x": "565", - "y": "570" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "552.5", - "y": "557.5" - }, - "name": "nth" - }, - { - "jdipNS:UNIT": { - "x": "664.2", - "y": "191.8" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "651.7", - "y": "179.3" - }, - "name": "nwg" - }, - { - "jdipNS:UNIT": { - "x": "715", - "y": "420" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "702.5", - "y": "407.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "761", - "y": "463" - }, - "name": "nwy" - }, - { - "jdipNS:UNIT": { - "x": "500", - "y": "855" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "487.5", - "y": "842.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "518", - "y": "819" - }, - "name": "par" - }, - { - "jdipNS:UNIT": { - "x": "535", - "y": "791" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "522.5", - "y": "778.5" - }, - "name": "pic" - }, - { - "jdipNS:UNIT": { - "x": "642", - "y": "978" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "629.5", - "y": "965.5" - }, - "name": "pie" - }, - { - "jdipNS:UNIT": { - "x": "193", - "y": "1023" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "180.5", - "y": "1010.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "151", - "y": "1060" - }, - "name": "por" - }, - { - "jdipNS:UNIT": { - "x": "877", - "y": "700" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "864.5", - "y": "687.5" - }, - "name": "pru" - }, - { - "jdipNS:UNIT": { - "x": "743", - "y": "1112" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "730.5", - "y": "1099.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "719", - "y": "1087" - }, - "name": "rom" - }, - { - "jdipNS:UNIT": { - "x": "648", - "y": "789" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "635.5", - "y": "776.5" - }, - "name": "ruh" - }, - { - "jdipNS:UNIT": { - "x": "1108", - "y": "977" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1095.5", - "y": "964.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "1083", - "y": "1014" - }, - "name": "rum" - }, - { - "jdipNS:UNIT": { - "x": "945", - "y": "1060" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "932.5", - "y": "1047.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "936", - "y": "1021" - }, - "name": "ser" - }, - { - "jdipNS:UNIT": { - "x": "1296", - "y": "855" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1283.5", - "y": "842.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "1267", - "y": "973" - }, - "name": "sev" - }, - { - "jdipNS:UNIT": { - "x": "844", - "y": "779" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "831.5", - "y": "766.5" - }, - "name": "sil" - }, - { - "jdipNS:UNIT": { - "x": "747", - "y": "528" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "734.5", - "y": "515.5" - }, - "name": "ska" - }, - { - "jdipNS:UNIT": { - "x": "1265", - "y": "1220" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1252.5", - "y": "1207.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "1132", - "y": "1222" - }, - "name": "smy" - }, - { - "jdipNS:UNIT": { - "x": "347", - "y": "1049" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "334.5", - "y": "1036.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "302", - "y": "1056" - }, - "name": "spa" - }, - { - "jdipNS:UNIT": { - "x": "1178", - "y": "415" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1165.5", - "y": "402.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "1113", - "y": "469" - }, - "name": "stp" - }, - { - "jdipNS:UNIT": { - "x": "841", - "y": "469" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "828.5", - "y": "456.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "890", - "y": "489" - }, - "name": "swe" - }, - { - "jdipNS:UNIT": { - "x": "642", - "y": "928" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "629", - "y": "915.5" - }, - "name": "swi" - }, - { - "jdipNS:UNIT": { - "x": "1464", - "y": "1216" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1451.5", - "y": "1203.5" - }, - "name": "syr" - }, - { - "jdipNS:UNIT": { - "x": "837", - "y": "1006" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "824.5", - "y": "993.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "794", - "y": "975" - }, - "name": "tri" - }, - { - "jdipNS:UNIT": { - "x": "634", - "y": "1310" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "621.5", - "y": "1297.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "647", - "y": "1282" - }, - "name": "tun" - }, - { - "jdipNS:UNIT": { - "x": "698", - "y": "1044" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "685.5", - "y": "1031.5" - }, - "name": "tus" - }, - { - "jdipNS:UNIT": { - "x": "720", - "y": "1160" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "707.5", - "y": "1147.5" - }, - "name": "tyn" - }, - { - "jdipNS:UNIT": { - "x": "754", - "y": "914" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "741.5", - "y": "901.5" - }, - "name": "tyr" - }, - { - "jdipNS:UNIT": { - "x": "710.0", - "y": "1159.1" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "697.5", - "y": "1146.6" - }, - "name": "tys" - }, - { - "jdipNS:UNIT": { - "x": "1136", - "y": "810" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1123.5", - "y": "797.5" - }, - "name": "ukr" - }, - { - "jdipNS:UNIT": { - "x": "719", - "y": "1004" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "706.5", - "y": "991.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "733", - "y": "971" - }, - "name": "ven" - }, - { - "jdipNS:UNIT": { - "x": "867", - "y": "874" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "854.5", - "y": "861.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "835", - "y": "887" - }, - "name": "vie" - }, - { - "jdipNS:UNIT": { - "x": "440", - "y": "668" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "427.5", - "y": "655.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "440", - "y": "712" - }, - "name": "wal" - }, - { - "jdipNS:UNIT": { - "x": "995", - "y": "750" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "982.5", - "y": "737.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "937", - "y": "748" - }, - "name": "war" - }, - { - "jdipNS:UNIT": { - "x": "474", - "y": "1173" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "461.5", - "y": "1160.5" - }, - "name": "wes" - }, - { - "jdipNS:UNIT": { - "x": "504", - "y": "626" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "491.5", - "y": "613.5" - }, - "jdipNS:SUPPLY_CENTER": { - "x": "506", - "y": "647" - }, - "name": "yor" - }, - { - "jdipNS:UNIT": { - "x": "1218", - "y": "222" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1205.5", - "y": "209.5" - }, - "name": "stp-nc" - }, - { - "jdipNS:UNIT": { - "x": "1066", - "y": "487" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1053.5", - "y": "474.5" - }, - "name": "stp-sc" - }, - { - "jdipNS:UNIT": { - "x": "1127", - "y": "1067" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1114.5", - "y": "1054.5" - }, - "name": "bul-ec" - }, - { - "jdipNS:UNIT": { - "x": "1070", - "y": "1140" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "1057.5", - "y": "1127.5" - }, - "name": "bul-sc" - }, - { - "jdipNS:UNIT": { - "x": "289", - "y": "965" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "276.5", - "y": "952.5" - }, - "name": "spa-nc" - }, - { - "jdipNS:UNIT": { - "x": "291", - "y": "1166" - }, - "jdipNS:DISLODGED_UNIT": { - "x": "278.5", - "y": "1153.5" - }, - "name": "spa-sc" - } - ] - } -}; diff --git a/diplomacy/web/src/gui/maps/standard/SvgStandardMetadata.js b/diplomacy/web/src/gui/maps/standard/SvgStandardMetadata.js new file mode 100644 index 0000000..7d8233c --- /dev/null +++ b/diplomacy/web/src/gui/maps/standard/SvgStandardMetadata.js @@ -0,0 +1,21 @@ +/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/ +export const Coordinates = {"ADR":{"unit":["793.5","1048.0"],"disl":["782.0","1038.0"]},"AEG":{"unit":["1043.5","1230.0"],"disl":["1032.0","1220.0"]},"ALB":{"unit":["906.5","1113.0"],"disl":["895.0","1103.0"]},"ANK":{"unit":["1301.5","1110.0"],"disl":["1290.0","1100.0"]},"APU":{"unit":["791.5","1106.0"],"disl":["780.0","1096.0"]},"ARM":{"unit":["1484.5","1090.0"],"disl":["1473.0","1080.0"]},"BAL":{"unit":["878.5","610.0"],"disl":["867.0","600.0"]},"BAR":{"unit":["1162.5","73.0"],"disl":["1151.0","63.0"]},"BEL":{"unit":["561.5","753.0"],"disl":["550.0","743.0"]},"BER":{"unit":["771.5","690.0"],"disl":["760.0","680.0"]},"BLA":{"unit":["1233.5","1000.0"],"disl":["1222.0","990.0"]},"BOH":{"unit":["806.5","814.0"],"disl":["795.0","804.0"]},"BOT":{"unit":["941.5","485.0"],"disl":["930.0","475.0"]},"BRE":{"unit":["404.5","819.0"],"disl":["393.0","809.0"]},"BUD":{"unit":["950.5","904.0"],"disl":["939.0","894.0"]},"BUL":{"unit":["1048.5","1068.0"],"disl":["1037.0","1058.0"]},"BUL/EC":{"unit":["1127.0","1067.0"],"disl":["1115.5","1057.0"]},"BUL/SC":{"unit":["1070.0","1140.0"],"disl":["1058.5","1130.0"]},"BUR":{"unit":["559.5","871.0"],"disl":["548.0","861.0"]},"CLY":{"unit":["436.5","492.0"],"disl":["425.0","482.0"]},"CON":{"unit":["1145.5","1137.0"],"disl":["1134.0","1127.0"]},"DEN":{"unit":["703.5","587.0"],"disl":["692.0","577.0"]},"EAS":{"unit":["1218.5","1311.0"],"disl":["1207.0","1301.0"]},"EDI":{"unit":["473.5","514.0"],"disl":["462.0","504.0"]},"ENG":{"unit":["394.5","751.0"],"disl":["383.0","741.0"]},"FIN":{"unit":["988.5","380.0"],"disl":["977.0","370.0"]},"GAL":{"unit":["999.5","831.0"],"disl":["988.0","821.0"]},"GAS":{"unit":["422.5","912.0"],"disl":["411.0","902.0"]},"GOL":{"unit":["556.0","1060.0"],"disl":["544.5","1050.0"]},"GRE":{"unit":["966.5","1190.0"],"disl":["955.0","1180.0"]},"HEL":{"unit":["651.5","631.0"],"disl":["640.0","621.0"]},"HOL":{"unit":["596.5","711.0"],"disl":["585.0","701.0"]},"ION":{"unit":["846.5","1286.0"],"disl":["835.0","1276.0"]},"IRI":{"unit":["335.5","661.0"],"disl":["324.0","651.0"]},"KIE":{"unit":["683.5","701.0"],"disl":["672.0","691.0"]},"LON":{"unit":["488.5","675.0"],"disl":["477.0","665.0"]},"LVN":{"unit":["1025.5","567.0"],"disl":["1014.0","557.0"]},"LVP":{"unit":["450.5","576.0"],"disl":["439.0","566.0"]},"LYO":{"unit":["514.3","1055.0"],"disl":["502.8","1045.0"]},"MAO":{"unit":["141.8","835.3"],"disl":["130.3","825.3"]},"MAR":{"unit":["524.5","975.0"],"disl":["513.0","965.0"]},"MID":{"unit":["126.0","902.0"],"disl":["114.5","892.0"]},"MOS":{"unit":["1200.5","590.0"],"disl":["1189.0","580.0"]},"MUN":{"unit":["693.5","828.0"],"disl":["682.0","818.0"]},"NAF":{"unit":["325.5","1281.0"],"disl":["314.0","1271.0"]},"NAO":{"unit":["180.1","288.2"],"disl":["168.6","278.2"]},"NAP":{"unit":["806.5","1170.0"],"disl":["795.0","1160.0"]},"NAT":{"unit":["238.0","427.0"],"disl":["226.5","417.0"]},"NRG":{"unit":["605.0","250.0"],"disl":["593.5","240.0"]},"NTH":{"unit":["553.5","560.0"],"disl":["542.0","550.0"]},"NWG":{"unit":["652.7","181.8"],"disl":["641.2","171.8"]},"NWY":{"unit":["703.5","410.0"],"disl":["692.0","400.0"]},"PAR":{"unit":["488.5","845.0"],"disl":["477.0","835.0"]},"PIC":{"unit":["523.5","781.0"],"disl":["512.0","771.0"]},"PIE":{"unit":["630.5","968.0"],"disl":["619.0","958.0"]},"POR":{"unit":["181.5","1013.0"],"disl":["170.0","1003.0"]},"PRU":{"unit":["865.5","690.0"],"disl":["854.0","680.0"]},"ROM":{"unit":["731.5","1102.0"],"disl":["720.0","1092.0"]},"RUH":{"unit":["636.5","779.0"],"disl":["625.0","769.0"]},"RUM":{"unit":["1096.5","967.0"],"disl":["1085.0","957.0"]},"SER":{"unit":["933.5","1050.0"],"disl":["922.0","1040.0"]},"SEV":{"unit":["1284.5","845.0"],"disl":["1273.0","835.0"]},"SIL":{"unit":["832.5","769.0"],"disl":["821.0","759.0"]},"SKA":{"unit":["735.5","518.0"],"disl":["724.0","508.0"]},"SMY":{"unit":["1253.5","1210.0"],"disl":["1242.0","1200.0"]},"SPA":{"unit":["335.5","1039.0"],"disl":["324.0","1029.0"]},"SPA/NC":{"unit":["289.0","965.0"],"disl":["277.5","955.0"]},"SPA/SC":{"unit":["291.0","1166.0"],"disl":["279.5","1156.0"]},"STP":{"unit":["1166.5","405.0"],"disl":["1155.0","395.0"]},"STP/NC":{"unit":["1218.0","222.0"],"disl":["1206.5","212.0"]},"STP/SC":{"unit":["1066.0","487.0"],"disl":["1054.5","477.0"]},"SWE":{"unit":["829.5","459.0"],"disl":["818.0","449.0"]},"SWI":{"unit":["642.0","928.0"],"disl":["630.5","918.0"]},"SYR":{"unit":["1452.5","1206.0"],"disl":["1441.0","1196.0"]},"TRI":{"unit":["825.5","996.0"],"disl":["814.0","986.0"]},"TUN":{"unit":["622.5","1300.0"],"disl":["611.0","1290.0"]},"TUS":{"unit":["686.5","1034.0"],"disl":["675.0","1024.0"]},"TYN":{"unit":["720.0","1160.0"],"disl":["708.5","1150.0"]},"TYR":{"unit":["742.5","904.0"],"disl":["731.0","894.0"]},"TYS":{"unit":["698.5","1149.1"],"disl":["687.0","1139.1"]},"UKR":{"unit":["1124.5","800.0"],"disl":["1113.0","790.0"]},"VEN":{"unit":["707.5","994.0"],"disl":["696.0","984.0"]},"VIE":{"unit":["855.5","864.0"],"disl":["844.0","854.0"]},"WAL":{"unit":["428.5","658.0"],"disl":["417.0","648.0"]},"WAR":{"unit":["983.5","740.0"],"disl":["972.0","730.0"]},"WES":{"unit":["462.5","1163.0"],"disl":["451.0","1153.0"]},"YOR":{"unit":["492.5","616.0"],"disl":["481.0","606.0"]}}; +export const SymbolSizes = {"Fleet":{"width":40.0,"height":40.0},"Army":{"width":40.0,"height":40.0},"Wing":{"width":40.0,"height":40.0},"DislodgedFleet":{"width":40.0,"height":40.0},"DislodgedArmy":{"width":40.0,"height":40.0},"DislodgedWing":{"width":40.0,"height":40.0},"FailedOrder":{"width":30.0,"height":30.0},"SupplyCenter":{"width":20.0,"height":20.0},"BuildUnit":{"width":60.0,"height":60.0},"RemoveUnit":{"width":50.0,"height":50.0},"WaivedBuild":{"width":40.0,"height":40.0},"HoldUnit":{"width":66.6,"height":66.6},"SupportHoldUnit":{"width":76.6,"height":76.6},"ConvoyTriangle":{"width":66.4,"height":57.4},"Stroke":{"width":6.0,"height":10.0}}; +export const Colors = {"AUSTRIA":"#c48f85","ENGLAND":"darkviolet","FRANCE":"royalblue","GERMANY":"#a08a75","ITALY":"forestgreen","RUSSIA":"#757d91","TURKEY":"#b9a61c"}; diff --git a/diplomacy/web/src/gui/maps/standard/build.js b/diplomacy/web/src/gui/maps/standard/build.js deleted file mode 100644 index cf28f19..0000000 --- a/diplomacy/web/src/gui/maps/standard/build.js +++ /dev/null @@ -1,53 +0,0 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -import React from "react"; -import {ARMY, Coordinates, FLEET, offset, SymbolSizes} from "./common"; -import PropTypes from "prop-types"; - -export class Build extends React.Component { - render() { - const loc = this.props.loc; - const unit_type = this.props.unitType; - const loc_x = offset(Coordinates[loc].unit[0], -11.5); - const loc_y = offset(Coordinates[loc].unit[1], -10.); - const build_loc_x = offset(Coordinates[loc].unit[0], -20.5); - const build_loc_y = offset(Coordinates[loc].unit[1], -20.5); - const symbol = unit_type === 'A' ? ARMY : FLEET; - const build_symbol = 'BuildUnit'; - return ( - - - - - ); - } -} - -Build.propTypes = { - unitType: PropTypes.string.isRequired, - loc: PropTypes.string.isRequired, - powerName: PropTypes.string.isRequired -}; diff --git a/diplomacy/web/src/gui/maps/standard/common.js b/diplomacy/web/src/gui/maps/standard/common.js deleted file mode 100644 index cb768c2..0000000 --- a/diplomacy/web/src/gui/maps/standard/common.js +++ /dev/null @@ -1,75 +0,0 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -import {SvgStandardExtra} from "./SvgStandardExtra"; - -const TAG_ORDERDRAWING = 'jdipNS:ORDERDRAWING'; -const TAG_POWERCOLORS = 'jdipNS:POWERCOLORS'; -const TAG_POWERCOLOR = 'jdipNS:POWERCOLOR'; -const TAG_SYMBOLSIZE = 'jdipNS:SYMBOLSIZE'; -const TAG_PROVINCE_DATA = 'jdipNS:PROVINCE_DATA'; -const TAG_PROVINCE = 'jdipNS:PROVINCE'; -const TAG_UNIT = 'jdipNS:UNIT'; -const TAG_DISLODGED_UNIT = 'jdipNS:DISLODGED_UNIT'; -const TAG_SUPPLY_CENTER = 'jdipNS:SUPPLY_CENTER'; - -export const ARMY = 'Army'; -export const FLEET = 'Fleet'; - -function getCoordinates() { - const coordinates = {}; - for (let provinceDefiniton of SvgStandardExtra[TAG_PROVINCE_DATA][TAG_PROVINCE]) { - const name = provinceDefiniton.name.toUpperCase().replace('-', '/'); - coordinates[name] = {}; - if (provinceDefiniton.hasOwnProperty(TAG_UNIT)) { - coordinates[name].unit = [provinceDefiniton[TAG_UNIT]['x'], provinceDefiniton[TAG_UNIT]['y']]; - } - if (provinceDefiniton.hasOwnProperty(TAG_DISLODGED_UNIT)) { - coordinates[name].disl = [provinceDefiniton[TAG_DISLODGED_UNIT]['x'], provinceDefiniton[TAG_DISLODGED_UNIT]['y']]; - } - if (provinceDefiniton.hasOwnProperty(TAG_SUPPLY_CENTER)) { - coordinates[name].sc = [provinceDefiniton[TAG_SUPPLY_CENTER]['x'], provinceDefiniton[TAG_SUPPLY_CENTER]['y']]; - } - } - return coordinates; -} - -function getSymbolSizes() { - const sizes = {}; - for (let definition of SvgStandardExtra[TAG_ORDERDRAWING][TAG_SYMBOLSIZE]) { - sizes[definition.name] = { - width: parseInt(definition.width), - height: parseInt(definition.height) - }; - } - return sizes; -} - -function getColors() { - const colors = {}; - for (let definition of SvgStandardExtra[TAG_ORDERDRAWING][TAG_POWERCOLORS][TAG_POWERCOLOR]) { - colors[definition.power.toUpperCase()] = definition.color; - } - return colors; -} - -export const Coordinates = getCoordinates(); -export const SymbolSizes = getSymbolSizes(); -export const Colors = getColors(); - -export function offset(floatString, offset) { - return "" + (parseFloat(floatString) + offset); -} diff --git a/diplomacy/web/src/gui/maps/standard/convoy.js b/diplomacy/web/src/gui/maps/standard/convoy.js deleted file mode 100644 index 03b4749..0000000 --- a/diplomacy/web/src/gui/maps/standard/convoy.js +++ /dev/null @@ -1,98 +0,0 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -import React from "react"; -import {Colors, Coordinates, offset} from "./common"; -import PropTypes from "prop-types"; - -export class Convoy extends React.Component { - render() { - const loc = this.props.loc; - const src_loc = this.props.srcLoc; - const dest_loc = this.props.dstLoc; - const loc_x = offset(Coordinates[loc].unit[0], 10); - const loc_y = offset(Coordinates[loc].unit[1], 10); - const src_loc_x = offset(Coordinates[src_loc].unit[0], 10); - const src_loc_y = offset(Coordinates[src_loc].unit[1], 10); - let dest_loc_x = offset(Coordinates[dest_loc].unit[0], 10); - let dest_loc_y = offset(Coordinates[dest_loc].unit[1], 10); - - const src_delta_x = parseFloat(src_loc_x) - parseFloat(loc_x); - const src_delta_y = parseFloat(src_loc_y) - parseFloat(loc_y); - const src_vector_length = Math.sqrt(src_delta_x * src_delta_x + src_delta_y * src_delta_y); - const src_loc_x_1 = '' + Math.round((parseFloat(loc_x) + (src_vector_length - 30.) / src_vector_length * src_delta_x) * 100.) / 100.; - const src_loc_y_1 = '' + Math.round((parseFloat(loc_y) + (src_vector_length - 30.) / src_vector_length * src_delta_y) * 100.) / 100.; - - let dest_delta_x = parseFloat(src_loc_x) - parseFloat(dest_loc_x); - let dest_delta_y = parseFloat(src_loc_y) - parseFloat(dest_loc_y); - let dest_vector_length = Math.sqrt(dest_delta_x * dest_delta_x + dest_delta_y * dest_delta_y); - const src_loc_x_2 = '' + Math.round((parseFloat(dest_loc_x) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_x) * 100.) / 100.; - const src_loc_y_2 = '' + Math.round((parseFloat(dest_loc_y) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_y) * 100.) / 100.; - - dest_delta_x = parseFloat(dest_loc_x) - parseFloat(src_loc_x); - dest_delta_y = parseFloat(dest_loc_y) - parseFloat(src_loc_y); - dest_vector_length = Math.sqrt(dest_delta_x * dest_delta_x + dest_delta_y * dest_delta_y); - dest_loc_x = '' + Math.round((parseFloat(src_loc_x) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_x) * 100.) / 100.; - dest_loc_y = '' + Math.round((parseFloat(src_loc_y) + (dest_vector_length - 30.) / dest_vector_length * dest_delta_y) * 100.) / 100.; - - const triangle_coord = []; - const triangle_loc_x = offset(Coordinates[src_loc].unit[0], 10); - const triangle_loc_y = offset(Coordinates[src_loc].unit[1], 10); - for (let ofs of [[0, -38.3], [33.2, 19.1], [-33.2, 19.1]]) { - triangle_coord.push(offset(triangle_loc_x, ofs[0]) + ',' + offset(triangle_loc_y, ofs[1])); - } - - return ( - - - - - - - - - ); - } -} - -Convoy.propTypes = { - loc: PropTypes.string.isRequired, - srcLoc: PropTypes.string.isRequired, - dstLoc: PropTypes.string.isRequired, - powerName: PropTypes.string.isRequired -}; diff --git a/diplomacy/web/src/gui/maps/standard/disband.js b/diplomacy/web/src/gui/maps/standard/disband.js deleted file mode 100644 index 857ba70..0000000 --- a/diplomacy/web/src/gui/maps/standard/disband.js +++ /dev/null @@ -1,51 +0,0 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -import React from "react"; -import {Coordinates, offset, SymbolSizes} from "./common"; -import PropTypes from "prop-types"; - -export class Disband extends React.Component { - render() { - const loc = this.props.loc; - const phaseType = this.props.phaseType; - let loc_x = 0; - let loc_y = 0; - if (phaseType === 'R') { - loc_x = offset(Coordinates[loc].unit[0], -29.); - loc_y = offset(Coordinates[loc].unit[1], -27.5); - } else { - loc_x = offset(Coordinates[loc].unit[0], -16.5); - loc_y = offset(Coordinates[loc].unit[1], -15.); - } - const symbol = 'RemoveUnit'; - return ( - - - - ); - } -} - -Disband.propTypes = { - loc: PropTypes.string.isRequired, - phaseType: PropTypes.string.isRequired -}; diff --git a/diplomacy/web/src/gui/maps/standard/hold.js b/diplomacy/web/src/gui/maps/standard/hold.js deleted file mode 100644 index 4a79deb..0000000 --- a/diplomacy/web/src/gui/maps/standard/hold.js +++ /dev/null @@ -1,45 +0,0 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -import React from "react"; -import {Colors, Coordinates, offset} from "./common"; -import PropTypes from "prop-types"; - -export class Hold extends React.Component { - render() { - const polygon_coord = []; - const loc_x = offset(Coordinates[this.props.loc].unit[0], 8.5); - const loc_y = offset(Coordinates[this.props.loc].unit[1], 9.5); - for (let ofs of [ - [13.8, -33.3], [33.3, -13.8], [33.3, 13.8], [13.8, 33.3], [-13.8, 33.3], - [-33.3, 13.8], [-33.3, -13.8], [-13.8, -33.3]] - ) { - polygon_coord.push(offset(loc_x, ofs[0]) + ',' + offset(loc_y, ofs[1])); - } - return ( - - - - - ); - } -} - -Hold.propTypes = { - loc: PropTypes.string.isRequired, - powerName: PropTypes.string.isRequired -}; diff --git a/diplomacy/web/src/gui/maps/standard/move.js b/diplomacy/web/src/gui/maps/standard/move.js deleted file mode 100644 index 8351a96..0000000 --- a/diplomacy/web/src/gui/maps/standard/move.js +++ /dev/null @@ -1,69 +0,0 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -import React from "react"; -import {Colors, Coordinates, offset} from "./common"; -import PropTypes from "prop-types"; - -export class Move extends React.Component { - render() { - const src_loc = this.props.srcLoc; - const dest_loc = this.props.dstLoc; - let src_loc_x = 0; - let src_loc_y = 0; - if (this.props.phaseType === 'R') { - src_loc_x = offset(Coordinates[src_loc].unit[0], -2.5); - src_loc_y = offset(Coordinates[src_loc].unit[1], -2.5); - } else { - src_loc_x = offset(Coordinates[src_loc].unit[0], 10); - src_loc_y = offset(Coordinates[src_loc].unit[1], 10); - } - let dest_loc_x = offset(Coordinates[dest_loc].unit[0], 10); - let dest_loc_y = offset(Coordinates[dest_loc].unit[1], 10); - - // Adjusting destination - const delta_x = parseFloat(dest_loc_x) - parseFloat(src_loc_x); - const delta_y = parseFloat(dest_loc_y) - parseFloat(src_loc_y); - const vector_length = Math.sqrt(delta_x * delta_x + delta_y * delta_y); - dest_loc_x = '' + Math.round((parseFloat(src_loc_x) + (vector_length - 30.) / vector_length * delta_x) * 100.) / 100.; - dest_loc_y = '' + Math.round((parseFloat(src_loc_y) + (vector_length - 30.) / vector_length * delta_y) * 100.) / 100.; - return ( - - - - - ); - } -} - -Move.propTypes = { - srcLoc: PropTypes.string.isRequired, - dstLoc: PropTypes.string.isRequired, - powerName: PropTypes.string.isRequired, - phaseType: PropTypes.string.isRequired -}; diff --git a/diplomacy/web/src/gui/maps/standard/supplyCenter.js b/diplomacy/web/src/gui/maps/standard/supplyCenter.js deleted file mode 100644 index 0663ede..0000000 --- a/diplomacy/web/src/gui/maps/standard/supplyCenter.js +++ /dev/null @@ -1,40 +0,0 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -import React from "react"; -import {Coordinates, offset, SymbolSizes} from "./common"; -import PropTypes from "prop-types"; - -export class SupplyCenter extends React.Component { - render() { - const symbol = 'SupplyCenter'; - const loc_x = offset(Coordinates[this.props.loc].sc[0], -8.5); - const loc_y = offset(Coordinates[this.props.loc].sc[1], -11.0); - return ( - - ); - } -} - -SupplyCenter.propTypes = { - loc: PropTypes.string.isRequired, - powerName: PropTypes.string -}; diff --git a/diplomacy/web/src/gui/maps/standard/supportHold.js b/diplomacy/web/src/gui/maps/standard/supportHold.js deleted file mode 100644 index bfa2656..0000000 --- a/diplomacy/web/src/gui/maps/standard/supportHold.js +++ /dev/null @@ -1,73 +0,0 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -import React from "react"; -import {Colors, Coordinates, offset} from "./common"; -import PropTypes from "prop-types"; - -export class SupportHold extends React.Component { - render() { - const loc = this.props.loc; - const dest_loc = this.props.dstLoc; - const loc_x = offset(Coordinates[loc].unit[0], 10); - const loc_y = offset(Coordinates[loc].unit[1], 10); - let dest_loc_x = offset(Coordinates[dest_loc].unit[0], 10); - let dest_loc_y = offset(Coordinates[dest_loc].unit[1], 10); - - const delta_x = parseFloat(dest_loc_x) - parseFloat(loc_x); - const delta_y = parseFloat(dest_loc_y) - parseFloat(loc_y); - const vector_length = Math.sqrt(delta_x * delta_x + delta_y * delta_y); - dest_loc_x = '' + Math.round((parseFloat(loc_x) + (vector_length - 35.) / vector_length * delta_x) * 100.) / 100.; - dest_loc_y = '' + Math.round((parseFloat(loc_y) + (vector_length - 35.) / vector_length * delta_y) * 100.) / 100.; - - const polygon_coord = []; - const poly_loc_x = offset(Coordinates[dest_loc].unit[0], 8.5); - const poly_loc_y = offset(Coordinates[dest_loc].unit[1], 9.5); - for (let ofs of [ - [15.9, -38.3], [38.3, -15.9], [38.3, 15.9], [15.9, 38.3], [-15.9, 38.3], [-38.3, 15.9], - [-38.3, -15.9], [-15.9, -38.3] - ]) { - polygon_coord.push(offset(poly_loc_x, ofs[0]) + ',' + offset(poly_loc_y, ofs[1])); - } - return ( - - - - - - - ); - } -} - -SupportHold.propTypes = { - loc: PropTypes.string.isRequired, - dstLoc: PropTypes.string.isRequired, - powerName: PropTypes.string.isRequired, -}; diff --git a/diplomacy/web/src/gui/maps/standard/supportMove.js b/diplomacy/web/src/gui/maps/standard/supportMove.js deleted file mode 100644 index e46abc3..0000000 --- a/diplomacy/web/src/gui/maps/standard/supportMove.js +++ /dev/null @@ -1,57 +0,0 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -import React from "react"; -import {Colors, Coordinates, offset} from "./common"; -import PropTypes from "prop-types"; - -export class SupportMove extends React.Component { - render() { - const loc = this.props.loc; - const src_loc = this.props.srcLoc; - const dest_loc = this.props.dstLoc; - const loc_x = offset(Coordinates[loc].unit[0], 10); - const loc_y = offset(Coordinates[loc].unit[1], 10); - const src_loc_x = offset(Coordinates[src_loc].unit[0], 10); - const src_loc_y = offset(Coordinates[src_loc].unit[1], 10); - let dest_loc_x = offset(Coordinates[dest_loc].unit[0], 10); - let dest_loc_y = offset(Coordinates[dest_loc].unit[1], 10); - - // Adjusting destination - const delta_x = parseFloat(dest_loc_x) - parseFloat(src_loc_x); - const delta_y = parseFloat(dest_loc_y) - parseFloat(src_loc_y); - const vector_length = Math.sqrt(delta_x * delta_x + delta_y * delta_y); - dest_loc_x = '' + Math.round((parseFloat(src_loc_x) + (vector_length - 30.) / vector_length * delta_x) * 100.) / 100.; - dest_loc_y = '' + Math.round((parseFloat(src_loc_y) + (vector_length - 30.) / vector_length * delta_y) * 100.) / 100.; - return ( - - - - - ); - } -} - -SupportMove.propTypes = { - loc: PropTypes.string.isRequired, - srcLoc: PropTypes.string.isRequired, - dstLoc: PropTypes.string.isRequired, - powerName: PropTypes.string.isRequired, -}; diff --git a/diplomacy/web/src/gui/maps/standard/unit.js b/diplomacy/web/src/gui/maps/standard/unit.js deleted file mode 100644 index f32a678..0000000 --- a/diplomacy/web/src/gui/maps/standard/unit.js +++ /dev/null @@ -1,45 +0,0 @@ -// ============================================================================== -// Copyright (C) 2019 - Philip Paquette, Steven Bocco -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Affero General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -// details. -// -// You should have received a copy of the GNU Affero General Public License along -// with this program. If not, see . -// ============================================================================== -import React from "react"; -import {ARMY, Coordinates, FLEET, offset, SymbolSizes} from "./common"; -import PropTypes from "prop-types"; - -export class Unit extends React.Component { - render() { - const split_unit = this.props.unit.split(/ +/); - const unit_type = split_unit[0]; - const loc = split_unit[1]; - const dislogged_type = this.props.isDislodged ? 'disl' : 'unit'; - const symbol = unit_type === 'F' ? FLEET : ARMY; - const loc_x = offset(Coordinates[loc][dislogged_type][0], -11.5); - const loc_y = offset(Coordinates[loc][dislogged_type][1], -10.0); - return ( - - ); - } -} - -Unit.propTypes = { - unit: PropTypes.string.isRequired, - powerName: PropTypes.string.isRequired, - isDislodged: PropTypes.bool.isRequired, -}; diff --git a/diplomacy/web/src/gui/pages/content_connection.jsx b/diplomacy/web/src/gui/pages/content_connection.jsx index 8cd1d6e..01b0650 100644 --- a/diplomacy/web/src/gui/pages/content_connection.jsx +++ b/diplomacy/web/src/gui/pages/content_connection.jsx @@ -56,6 +56,8 @@ export class ContentConnection extends React.Component { return channel.getAvailableMaps(); }) .then(availableMaps => { + for (let mapName of Object.keys(availableMaps)) + availableMaps[mapName].powers.sort(); page.availableMaps = availableMaps; const userGameIndices = DipStorage.getUserGames(page.channel.username); if (userGameIndices && userGameIndices.length) { diff --git a/diplomacy/web/src/gui/pages/content_game.jsx b/diplomacy/web/src/gui/pages/content_game.jsx index 5392336..038c68d 100644 --- a/diplomacy/web/src/gui/pages/content_game.jsx +++ b/diplomacy/web/src/gui/pages/content_game.jsx @@ -44,6 +44,9 @@ import {saveGameToDisk} from "../utils/saveGameToDisk"; import {Game} from '../../diplomacy/engine/game'; import {PowerOrdersActionBar} from "../components/power_orders_actions_bar"; import {SvgStandard} from "../maps/standard/SvgStandard"; +import {SvgAncMed} from "../maps/ancmed/SvgAncMed"; +import {SvgModern} from "../maps/modern/SvgModern"; +import {SvgPure} from "../maps/pure/SvgPure"; import {MapData} from "../utils/map_data"; import {Queue} from "../../diplomacy/utils/queue"; @@ -77,6 +80,21 @@ const PRETTY_ROLES = { [STRINGS.OBSERVER_TYPE]: 'Observer' }; +const MAP_COMPONENTS = { + ancmed: SvgAncMed, + standard: SvgStandard, + modern: SvgModern, + pure: SvgPure +}; + +function getMapComponent(mapName) { + for (let rootMap of Object.keys(MAP_COMPONENTS)) { + if (mapName.indexOf(rootMap) === 0) + return MAP_COMPONENTS[rootMap]; + } + throw new Error(`Un-implemented map: ${mapName}`); +} + function noPromise() { return new Promise(resolve => resolve()); } @@ -120,7 +138,8 @@ export class ContentGame extends React.Component { orders: orders, // {power name => {loc => {local: bool, order: str}}} power: null, orderBuildingType: null, - orderBuildingPath: [] + orderBuildingPath: [], + showAbbreviations: true }; // Bind some class methods to this instance. @@ -140,6 +159,7 @@ export class ContentGame extends React.Component { this.onChangePastPhase = this.onChangePastPhase.bind(this); this.onChangePastPhaseIndex = this.onChangePastPhaseIndex.bind(this); this.onChangeShowPastOrders = this.onChangeShowPastOrders.bind(this); + this.onChangeShowAbbreviations = this.onChangeShowAbbreviations.bind(this); this.onChangeTabCurrentMessages = this.onChangeTabCurrentMessages.bind(this); this.onChangeTabPastMessages = this.onChangeTabPastMessages.bind(this); this.onClickMessage = this.onClickMessage.bind(this); @@ -360,7 +380,7 @@ export class ContentGame extends React.Component { if (this.networkGameIsDisplayed(networkGame)) { this.__store_orders(null); this.reloadDeadlineTimer(networkGame); - return this.setState({orders: null, messageHighlights: {}}) + return this.setState({orders: null, messageHighlights: {}, orderBuildingPath: []}) .then(() => this.getPage().info( `Game update (${notification.name}) to ${networkGame.local.phase}.`)); } @@ -582,10 +602,12 @@ export class ContentGame extends React.Component { * Reset local orders and replace them with current server orders for current selected power. */ reloadServerOrders() { - const currentPowerName = this.getCurrentPowerName(); - if (currentPowerName) { - this.reloadPowerServerOrders(currentPowerName); - } + this.setState({orderBuildingPath: []}).then(() => { + const currentPowerName = this.getCurrentPowerName(); + if (currentPowerName) { + this.reloadPowerServerOrders(currentPowerName); + } + }); } /** @@ -818,6 +840,10 @@ export class ContentGame extends React.Component { return this.setState({historyShowOrders: event.target.checked}); } + onChangeShowAbbreviations(event) { + return this.setState({showAbbreviations: event.target.checked}); + } + onClickMessage(message) { if (!message.read) { message.read = true; @@ -943,32 +969,37 @@ export class ContentGame extends React.Component { } renderMapForResults(gameEngine, showOrders) { + const Map = getMapComponent(gameEngine.map_name); return (
- +
); } renderMapForMessages(gameEngine, showOrders) { + const Map = getMapComponent(gameEngine.map_name); return (
- +
); } renderMapForCurrent(gameEngine, powerName, orderType, orderPath) { + const Map = getMapComponent(gameEngine.map_name); const rawOrders = this.__get_orders(gameEngine); const orders = {}; for (let entry of Object.entries(rawOrders)) { @@ -980,15 +1011,16 @@ export class ContentGame extends React.Component { } return (
- +
); } @@ -1015,23 +1047,27 @@ export class ContentGame extends React.Component { __form_phases(pastPhases, phaseIndex) { return ( -
-
+ ); } else { @@ -43,7 +46,7 @@ export class PanelChooseMap extends React.Component { const defaultVariant = variants[0]; mapEntries.push(
-
+
-
- {(() => { - const choice = []; - for (let i = 0; i < this.props.nbPowers; ++i) { - choice.push( - - ); - } - return choice; - })()} +
+
+
+ {(() => { + const choice = []; + for (let i = 0; i < this.props.nbPowers; ++i) { + choice.push( + + ); + } + return choice; + })()} +
+
-
- +
+
+ +
); diff --git a/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePower.js b/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePower.js index 1463805..8258acd 100644 --- a/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePower.js +++ b/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePower.js @@ -41,29 +41,35 @@ export class PanelChoosePower extends React.Component {
-
- {(() => { - const choice = []; - for (let i = 0; i < this.props.powers.length; ++i) { - choice.push( - - ); - } - return choice; - })()} +
+
+
+ {(() => { + const choice = []; + for (let i = 0; i < this.props.powers.length; ++i) { + choice.push( + + ); + } + return choice; + })()} +
+
-
- +
+
+ +
); diff --git a/diplomacy/web/src/index.css b/diplomacy/web/src/index.css index 77b5512..664dbb7 100644 --- a/diplomacy/web/src/index.css +++ b/diplomacy/web/src/index.css @@ -246,16 +246,6 @@ span.power-name { content: ', '; } -.fancy-wrapper { - position: fixed; - left: 0; - top: 0; - width: 100%; - height: 100%; - padding-top: 10%; - background-color: rgba(100, 100, 110, 0.5); -} - .fancy-box { border: 1px solid silver; position: relative; @@ -267,12 +257,7 @@ span.power-name { border-bottom: 1px solid silver; } -.fancy-box .fancy-button { -} - .fancy-box .fancy-content { - padding-top: 10px; - padding-bottom: 10px; background-color: white; } @@ -436,20 +421,13 @@ button.collapsed .roll { display: none; } -.panel-choose-map .map-list { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - overflow: auto; - border: 1px solid gray; -} - .inline { white-space: nowrap; } +.visibilityHidden { + visibility: hidden; +} /** Page login. **/ /** Page games. **/ diff --git a/diplomacy/web/svg_to_react.py b/diplomacy/web/svg_to_react.py index 12a68b6..dafe4d3 100644 --- a/diplomacy/web/svg_to_react.py +++ b/diplomacy/web/svg_to_react.py @@ -19,11 +19,122 @@ """ import argparse import os -import sys +import re from xml.dom import minidom, Node import ujson as json +LICENSE_TEXT = """/** +============================================================================== +Copyright (C) 2019 - Philip Paquette, Steven Bocco + + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . +============================================================================== +**/""" + +TAG_ORDERDRAWING = 'jdipNS:ORDERDRAWING' +TAG_POWERCOLORS = 'jdipNS:POWERCOLORS' +TAG_POWERCOLOR = 'jdipNS:POWERCOLOR' +TAG_SYMBOLSIZE = 'jdipNS:SYMBOLSIZE' +TAG_PROVINCE_DATA = 'jdipNS:PROVINCE_DATA' +TAG_PROVINCE = 'jdipNS:PROVINCE' +TAG_UNIT = 'jdipNS:UNIT' +TAG_DISLODGED_UNIT = 'jdipNS:DISLODGED_UNIT' +TAG_SUPPLY_CENTER = 'jdipNS:SUPPLY_CENTER' + +SELECTOR_REGEX = re.compile(r'([\r\n][ \t]*)([^{\r\n]+){') +LINES_REGEX = re.compile(r'[\r\n]+') +SPACES_REGEX = re.compile(r'[\t ]+') +STRING_REGEX = re.compile(r'[`\'"] {0,1}\+ {0,1}[`\'"]') + + +def prepend_css_selectors(prefix, css_text): + """ Prepend all CSS selector with given prefix (e.g. ID selector) followed by a space. + :param prefix: prefix to prepend + :param css_text: CSS text to parse + :rtype: str + """ + def repl(match): + return '%s%s %s{' % (match.group(1), prefix, match.group(2)) + + return SELECTOR_REGEX.sub(repl, css_text) + + +class ExtractedData: + """ Helper class to store extra data collected while parsing SVG file. Properties: + - name: class name of parsed SVG component + - extra: data parsed from invalid tags found in SVG content + - style_lines: string lines parsed from