Settlers of EMF

Beginnings of an EMF themed implementation of Settlers of Catan
master
Mat Booth 2018-09-15 15:49:48 +01:00
parent 4cbfd23e05
commit 57ee3129ac
3 changed files with 928 additions and 0 deletions

775
settlers_of_emf/main.py Normal file
View File

@ -0,0 +1,775 @@
"""Settlers of EMF
After a long voyage of great deprivation of wifi, your vehicles have finally reached the edge of an uncharted field at the foot of a great castle. The Electromagnetic Field! But you are not the only discoverer. Other fearless voyagers have also arrived at the foot the castle: the race to settle the Electricmagnetic Field has begun! """
___title___ = "Settlers of EMF"
___license___ = "MIT"
___dependencies___ = ["sleep"]
___categories___ = ["Games"]
___bootstrapped___ = False
import random, ugfx, math, time
from app import restart_to_default
from tilda import Buttons
ugfx.init()
# Because the button constants have no kind of order, it's hard to iterate
# through them, so keep a global list here to help us keep the code concise
BUTTONS = [
Buttons.BTN_1,
Buttons.BTN_2,
Buttons.BTN_3,
Buttons.BTN_4,
Buttons.BTN_5,
Buttons.BTN_6,
Buttons.BTN_7,
Buttons.BTN_8,
Buttons.BTN_9,
]
class State:
def run(self):
"""Runs the main loop for this state"""
self.done = False
self.redraw = False
self.selection = 0
# Render the current screen
self.draw()
# Enter the loop and spin until the user makes a choice
self.initialise()
while not self.done:
time.sleep_ms(10)
if self.redraw:
self.draw()
self.redraw = False
self.deinitialise()
# Then drop back out into the state machine returning the selected choice
return self.selection
def draw(self):
"""Renders the current state to the screen"""
pass
def initialise(self):
"""Perform actions that need to happen before we enter the loop"""
pass
def deinitialise(self):
"""Perform actions that need to happen after we exit the loop"""
pass
class Menu(State):
def __init__(self, question, choices):
self.question = question
self.choices = choices
def draw(self):
# Draw the menu on screen
ugfx.clear(ugfx.BLACK)
ugfx.display_image(0, 0, 'settlers_game/title.png')
ugfx.text(5, 95, self.question, ugfx.WHITE)
i = 0
for c in self.choices:
col = ugfx.WHITE
if 'colour' in c:
col = c['colour']
if 'disabled' in c and c['disabled']:
col = ugfx.html_color(0x676767)
ugfx.text(20, (20 * i) + 125, "{} - {} ".format(i + 1, c['name']), col)
i = i + 1
# Set the initial selection
self._set_selection(self.selection)
def initialise(self):
# Register callbacks
Buttons.enable_interrupt(Buttons.BTN_A, self._button_callback)
Buttons.enable_interrupt(Buttons.JOY_Up, self._button_callback)
Buttons.enable_interrupt(Buttons.JOY_Down, self._button_callback)
for i in range(len(self.choices)):
c = self.choices[i]
if 'disabled' not in c or not c['disabled']:
Buttons.enable_interrupt(BUTTONS[i], self._button_callback)
def deinitialise(self):
# Unregister callbacks
Buttons.disable_interrupt(Buttons.BTN_A)
Buttons.disable_interrupt(Buttons.JOY_Up)
Buttons.disable_interrupt(Buttons.JOY_Down)
for i in range(len(self.choices)):
c = self.choices[i]
if 'disabled' not in c or not c['disabled']:
Buttons.disable_interrupt(BUTTONS[i])
def _button_callback(self, btn):
if btn == Buttons.BTN_A:
self.done = True
else:
if btn == Buttons.JOY_Up:
new_selection = self._prev_valid_selection(self.selection)
elif btn == Buttons.JOY_Down:
new_selection = self._next_valid_selection(self.selection)
else:
for i in range(len(self.choices)):
if btn == BUTTONS[i]:
new_selection = i
self._set_selection(new_selection)
def _prev_valid_selection(self, sel):
# Calculate the next enabled option going upwards
if sel == 0:
next_sel = len(self.choices) - 1
else:
next_sel = sel - 1
while True:
c = self.choices[next_sel]
if 'disabled' not in c or not c['disabled']:
break
if next_sel == 0:
next_sel = len(self.choices) - 1
else:
next_sel = next_sel - 1
return next_sel
def _next_valid_selection(self, sel):
# Calculate the next enabled option going downwards
if sel == len(self.choices) - 1:
next_sel = 0
else:
next_sel = sel + 1
while True:
c = self.choices[next_sel]
if 'disabled' not in c or not c['disabled']:
break
if next_sel == len(self.choices) - 1:
next_sel = 0
else:
next_sel = next_sel + 1
return next_sel
def _set_selection(self, new_selection):
# Redraws the selection box
ugfx.box(0, (20 * self.selection) + 125, 240, 20, ugfx.BLACK)
self.selection = new_selection
ugfx.box(0, (20 * self.selection) + 125, 240, 20, ugfx.WHITE)
class MainMenu(Menu):
NEW_GAME = 0
CONTINUE_GAME = 1
EXIT = 2
options = [
{'name': "Start New Game"},
{'name': "Continue Game"},
{'name': "Exit"},
]
def __init__(self, disable_continue_option=True):
MainMenu.options[1]['disabled'] = disable_continue_option
super().__init__('Main menu:', MainMenu.options)
class TeamMenu(Menu):
BACK = 6
options = [
{'name': "Scottish Consulate",
'colour': ugfx.html_color(0x0000ff)},
{'name': "Camp Holland",
'colour': ugfx.html_color(0xff8c00)},
{'name': "Sheffield Hackers",
'colour': ugfx.html_color(0x26c6da)},
{'name': "Milliways",
'colour': ugfx.html_color(0xff00ff)},
{'name': "Robot Arms",
'colour': ugfx.html_color(0xeaeaea)},
{'name': "Null Sector",
'colour': ugfx.html_color(0x9c27b0)},
{'name': "Back"},
]
def __init__(self):
super().__init__('Choose your team:', TeamMenu.options)
def get_selected_team(self):
return TeamMenu.options[self.selection].copy()
class BuildMenu(Menu):
BACK = 0
options = [
{'name': "Road"},
{'name': "Town"},
{'name': "City"},
{'name': "Back"},
]
def __init__(self):
super().__init__('Build a thing:', BuildMenu.options)
# TODO: show the build cost
# TODO: enable options based on whether the player can afford them
class Hex:
"""Hexes are the games tiles. They have a resource kind, correspond to the value
of a roll of two D6 and may or may not contain the robber."""
# Screen coords are x,y values that locate pixels on the physical display:
#
# 0,0 → → 240,0
# ↓ ↓
# 0,320 → 240,320
#
# Hex coords are x,y,z values that locate the relative positions of hexagons:
#
# 0,1,-1
# -1,1,0 ↖ ↑ ↗ 1,0,-1
# 0,0,0
# -1,0,1 ↙ ↓ ↘ 1,-1,0
# 0,-1,1
#
# Converting between the two systems can be done by multiplying the x and y
# coordinates against a matrix. When converting to hex coords, the z value
# can be computed from the new x and y values because x + y + z must always
# equal zero.
#
# This is the matrix used to convert from hex coords to screen coords
matrix = [3.0 * 0.5, 0.0, math.sqrt(3.0) * 0.5, math.sqrt(3.0)]
# The screen coordinate to use as the origin for hex coordinates,
# the centre of hex 0,0,0 will be at this coordinate
origin = [math.ceil(ugfx.width() / 2), math.ceil(ugfx.height() / 2)]
# Size in pixels of the hex, from the centre point to each corner
size = 25
# Transformations for how to get to the neighbouring hexes
directions = {
0: [-1, 1, 0], # South West
1: [0, 1, -1], # South
2: [1, 0, -1], # South East
3: [1, -1, 0], # North East
4: [0, -1, 1], # North
5: [-1, 0, 1], # North West
}
def __init__(self, coords, resource, number, robber):
"""Create a new hex at the given hex coordinates, of the given kind of resource"""
# Validate coords
assert len(coords) == 3, 'Invalid number of hexagon coordinates'
assert coords[0] + coords[1] + coords[2] == 0, 'Invalid hexagon coordinate values'
self.coords = coords
# The kind of resource hosted by this hex
self.resource = resource
# The dice roll required to win this resource
self.number = number
# Whether this hex contains the robber
self.robber = robber
# Whether this hex should be highlighted
self.highlight = False
# A hex is quite expensive to draw, so keep track of whether the state changed recently
# to avoid redrawing where possible
self.changed = True
# Compute the screen coordinates of the centre of the hex
x = self.coords[0]
y = self.coords[1]
newX = (Hex.matrix[0] * x + Hex.matrix[1] * y) * Hex.size
newY = (Hex.matrix[2] * x + Hex.matrix[3] * y) * Hex.size
self.centre = [newX + Hex.origin[0], newY + Hex.origin[1]]
# Generate the list of screen coordinates for each of the corners of the hex
self.nodes = []
for i in range(0, 6):
angle = 2.0 * math.pi * (0 - i) / 6
offset = [Hex.size * math.cos(angle), Hex.size * math.sin(angle)]
self.nodes.append([int(self.centre[0] + offset[0]), int(self.centre[1] + offset[1])])
# Generate the list of pairs of screen coordinates for each of the sides of the hex
self.edges = []
for i in range(0, 6):
node1 = self.nodes[i]
if i < 5:
node2 = self.nodes[i + 1]
else:
node2 = self.nodes[0]
if node1[0] <= node2[0]:
self.edges.append([node1, node2])
else:
self.edges.append([node2, node1])
def set_highlight(self, highlight):
if self.highlight != highlight:
self.highlight = highlight
self.changed = True
@staticmethod
def get_neighbouring_hex_coords(coords, direction):
return [a + b for a, b in zip(coords, Hex.directions[direction])]
def draw(self):
if self.changed:
self.changed = False
ugfx.fill_polygon(0, 0, self.nodes, self.resource['col'])
if self.highlight:
text_colour = ugfx.WHITE
else:
text_colour = ugfx.BLACK
text_offset = Hex.size * 0.45
if self.robber:
ugfx.text(round(self.centre[0] - Hex.size * 0.75), round(self.centre[1] - text_offset), "Rob ", text_colour)
else:
if self.resource['kind'] != 5:
ugfx.text(round(self.centre[0] - text_offset), round(self.centre[1] - text_offset), "{} ".format(self.number['roll']), text_colour)
class Settlement:
"""A node at which it is possible to build a settlement."""
# Possible things this location may contain, the values here are the number of
# victory points that the building is worth to the player who built it
EMPTY = 0
TOWN = 1
CITY = 2
def __init__(self, node):
# Screen coords that define the settlement
self.node = node
# The list of hexes to which this settlement is adjacent
self.hexes = []
# What is built here and who owns it
self.team = None
self.contents = Settlement.EMPTY
def is_empty(self):
return self.contents == Settlement.EMPTY
def prob_score(self):
"""The probability score of the location is the sum of the probability of all adjacent hexes"""
score = 0
for h in self.hexes:
score = score + h.number['prob']
return score
def build_town(self, team):
assert self.contents == Settlement.EMPTY, 'Town can only be built in empty location'
self.team = team
self.contents = Settlement.TOWN
def build_city(self, team):
assert self.contents == Settlement.TOWN and self.team['name'] == team['name'], 'City can only be built in place of one of your own towns'
self.contents = Settlement.CITY
def draw(self):
if self.contents == Settlement.TOWN:
ugfx.fill_circle(self.node[0], self.node[1], 4, self.team['colour'])
ugfx.circle(self.node[0], self.node[1], 4, ugfx.WHITE)
elif self.contents == Settlement.CITY:
ugfx.fill_circle(self.node[0], self.node[1], 8, self.team['colour'])
ugfx.circle(self.node[0], self.node[1], 8, ugfx.WHITE)
class Road:
"""An edge along which it is possible to build a road."""
EMPTY = 0
ROAD = 1
def __init__(self, edge):
# List of screen coords that define the road
self.edge = edge
# What is built here and who owns it
self.team = None
self.contents = Road.EMPTY
def is_empty(self):
return self.contents == Road.EMPTY
def build_road(self, team):
assert self.contents == Road.EMPTY, 'Road can only be built in empty location'
self.team = team
self.contents = Road.ROAD
def draw(self):
if self.contents == Road.ROAD:
ugfx.thickline(self.edge[0][0], self.edge[0][1], self.edge[1][0], self.edge[1][1], ugfx.WHITE, 6, False)
ugfx.thickline(self.edge[0][0], self.edge[0][1], self.edge[1][0], self.edge[1][1], self.team['colour'], 4, False)
class Player:
"""The player's hand of resource cards and their score and what not."""
def __init__(self, team, roads, settlements, resources):
# Player team details
self.team = team
# All the buildable game board locations
self.roads = roads
self.settlements = settlements
# Player resources
self.resources = []
for r in resources.copy():
r['quantity'] = 0
self.resources.append(r)
# Collect starting resources from the hexes adjacent to our starting settlements
for s in [x for x in self.settlements if x.team == self.team]:
for h in s.hexes:
if r['kind'] == h.resource['kind']:
r['quantity'] = r['quantity'] + 1
def score(self):
points = 0
for s in [x for x in self.settlements if x.team == self.team]:
points = points + s.contents
return points
def draw(self):
# Player's team and score
ugfx.text(5, 8, "{} ".format(self.team['name']), self.team['colour'])
ugfx.text(5, 28, "Points: {} ".format(self.score()), ugfx.WHITE)
# Player's resources
ugfx.area(0, 280, 120, 40, ugfx.BLACK)
offset = int(ugfx.width() / len(self.resources))
square = int(offset / 3)
for i in range(len(self.resources)):
ugfx.area((offset * i) + 1, 285, square, 22, self.resources[i]['col'])
ugfx.text((offset * i) + 1 + square, 285, "{} ".format(self.resources[i]['quantity']), ugfx.WHITE)
class Dice:
# Size in pixels that the dice will be drawn on screen
size = 25
def __init__(self):
self.reset()
def roll(self):
self.die1 = random.randint(1, 6)
self.die2 = random.randint(1, 6)
def reset(self):
self.die1 = 0
self.die2 = 0
def total(self):
return self.die1 + self.die2
def draw(self):
self._draw_die(210, 5, self.die1)
self._draw_die(210, 5 + Dice.size + 5, self.die2)
def _draw_die(self, x, y, num):
ugfx.box(x, y, Dice.size, Dice.size, ugfx.html_color(0x676767))
ugfx.area(x + 1, y + 1, Dice.size - 2, Dice.size - 2, ugfx.BLACK)
if num == 1:
self._draw_one_dot(x, y)
if num == 2:
self._draw_two_dot(x, y)
if num == 3:
self._draw_one_dot(x, y)
self._draw_two_dot(x, y)
if num == 4:
self._draw_four_dot(x, y)
if num == 5:
self._draw_one_dot(x, y)
self._draw_four_dot(x, y)
if num == 6:
self._draw_six_dot(x, y)
def _draw_one_dot(self, x, y):
ugfx.fill_circle(x + int(Dice.size / 2), y + int(Dice.size / 2), 1, ugfx.WHITE)
def _draw_two_dot(self, x, y):
ugfx.fill_circle(1 + x + int(Dice.size / 8), (y - 1) + (Dice.size - int(Dice.size / 8)), 1, ugfx.WHITE)
ugfx.fill_circle((x - 2) + (Dice.size - int(Dice.size / 8)), 1 + y + int(Dice.size / 8), 1, ugfx.WHITE)
def _draw_four_dot(self, x, y):
self._draw_two_dot(x, y)
ugfx.fill_circle(1 + x + int(Dice.size / 8), 1 + y + int(Dice.size / 8), 1, ugfx.WHITE)
ugfx.fill_circle((x - 2) + (Dice.size - int(Dice.size / 8)), (y - 1) + (Dice.size - int(Dice.size / 8)), 1, ugfx.WHITE)
def _draw_six_dot(self, x, y):
self._draw_four_dot(x, y)
ugfx.fill_circle(1 + x + int(Dice.size / 8), y + int(Dice.size / 2), 1, ugfx.WHITE)
ugfx.fill_circle((x - 2) + (Dice.size - int(Dice.size / 8)), y + int(Dice.size / 2), 1, ugfx.WHITE)
class GameBoard(State):
MAIN_MENU = 0
BUILD_MENU = 1
END_TURN = 2
# Kinds of resource
SHEEP = {'kind':0, 'col': ugfx.html_color(0xd4e157)}
WHEAT = {'kind':1, 'col': ugfx.html_color(0xffc107)}
WOOD = {'kind':2, 'col': ugfx.html_color(0x993300)}
BRICK = {'kind':3, 'col': ugfx.html_color(0xff0000)}
ORE = {'kind':4, 'col': ugfx.html_color(0x757575)}
DESERT = {'kind':5, 'col': ugfx.html_color(0xffee55)} # Not really a resource
# List of resources (pre-randomised to combat the not-very random number
# generator) that make up the hexes on the game board for 4 players
resources = [ORE, SHEEP, WHEAT, ORE, ORE, WOOD, DESERT, BRICK, SHEEP, WOOD,
WHEAT, WOOD, WOOD, WHEAT, SHEEP, BRICK, SHEEP, BRICK, WHEAT]
# Dice roll probabilities
TWO = {'roll':2, 'prob':1}
THREE = {'roll':3, 'prob':2}
FOUR = {'roll':4, 'prob':3}
FIVE = {'roll':5, 'prob':4}
SIX = {'roll':6, 'prob':5}
SEVEN = {'roll':7, 'prob':0} # Most probable, but zero because desert
EIGHT = {'roll':8, 'prob':5}
NINE = {'roll':9, 'prob':4}
TEN = {'roll':10, 'prob':3}
ELEVEN = {'roll':11, 'prob':2}
TWELVE = {'roll':12, 'prob':1}
# Dice rolls for (these have a strict order) to be assigned to the resource
# hexes for 4 player games
numbers = [FIVE, TWO, SIX, THREE, EIGHT, TEN, NINE, TWELVE, ELEVEN, FOUR,
EIGHT, TEN, NINE, FOUR, FIVE, SIX, THREE, ELEVEN]
def __init__(self, team):
# Two rings of hexes around the centre
radius = 2
# Choose a starting hex on the outermost ring of hexes
choice = random.randrange(0, 6)
coords = [0, 0, 0]
for i in range(radius):
coords = [a + b for a, b in zip(coords, Hex.directions[choice])]
# Copy lists so we can edit them with impunity
r_copy = GameBoard.resources.copy()
n_copy = GameBoard.numbers.copy()
self.hexes = []
while radius > 0:
# From the starting hex, go radius hexes in each of the 6 directions
for i in list(range((choice + 2) % 6, 6)) + list(range(0, (choice + 2) % 6)):
for _ in range(radius):
# The resources are picked at random from the list
resource = r_copy.pop(random.randrange(0, len(r_copy)))
# But the dice roll numbers are picked in order, unless it's
# the desert in which case that is always 7
number = GameBoard.SEVEN
if resource['kind'] != 5:
number = n_copy.pop(0)
self.hexes.append(Hex(coords, resource, number, number['roll'] == 7))
coords = Hex.get_neighbouring_hex_coords(coords, i)
# Go into the next ring of hexes (opposite direction of starting choice)
coords = Hex.get_neighbouring_hex_coords(coords, (choice + 3) % 6)
radius = radius - 1
# The final, centre hex
resource = r_copy.pop()
number = GameBoard.SEVEN
if resource['kind'] != 5:
number = n_copy.pop(0)
self.hexes.append(Hex(coords, resource, number, number['roll'] == 7))
# Generate lists of unique valid locations for building
self.roads = []
self.settlements = []
for h in self.hexes:
for edge in h.edges:
already_got = False
for r in self.roads:
if r.edge == edge:
already_got = True
if not already_got:
r = Road(edge)
self.roads.append(r)
for node in h.nodes:
already_got = False
for s in self.settlements:
if s.node == node:
already_got = True
s.hexes.append(h)
if not already_got:
s = Settlement(node)
s.hexes.append(h)
self.settlements.append(s)
# Give the team starting towns in the two settlements with the highest probability score
# TODO interleave starting town choices for multi-player
self.pick_starting_settlement(team)
self.pick_starting_settlement(team)
# The dice roller
self.dice = Dice()
# The player details
self.player = Player(team, self.roads, self.settlements, [GameBoard.SHEEP, GameBoard.WHEAT, GameBoard.WOOD, GameBoard.BRICK, GameBoard.ORE])
def get_roads_for_settlement(self, settlement):
"""Return a list of roads that connect to the given settlement"""
roads = []
for road in self.roads:
if settlement.node in road.edge:
roads.append(road)
return roads
def pick_starting_settlement(self, team):
"""Choose a starting settlement for the given team, and place a town and a connecting road there"""
# Sort the settlements by highest dice roll probability
sorted_settlements = sorted(self.settlements, key=lambda s: s.prob_score())
sorted_settlements.reverse()
# Build at the highest probability settlement that is still available
for s in sorted_settlements:
# TODO check the towns are not too close to one another
if s.is_empty():
s.build_town(team)
s_roads = self.get_roads_for_settlement(s)
s_roads[random.randrange(0, len(s_roads))].build_road(team)
break
def draw(self):
if not self.redraw:
ugfx.clear(ugfx.BLACK)
self.dice.draw()
for h in self.hexes:
h.draw()
for r in self.roads:
r.draw()
for s in self.settlements:
s.draw()
self.player.draw()
def initialise(self):
# Register callbacks
Buttons.enable_interrupt(Buttons.BTN_Menu, self._button_callback)
Buttons.enable_interrupt(Buttons.BTN_B, self._button_callback)
Buttons.enable_interrupt(Buttons.BTN_Star, self._button_callback)
Buttons.enable_interrupt(Buttons.BTN_Hash, self._button_callback)
def deinitialise(self):
# Unregister callbacks
Buttons.disable_interrupt(Buttons.BTN_Menu)
Buttons.disable_interrupt(Buttons.BTN_B)
Buttons.disable_interrupt(Buttons.BTN_Star)
Buttons.disable_interrupt(Buttons.BTN_Hash)
# Ensure all hexes are drawn next time we enter this state
for h in self.hexes:
h.changed = True
def _button_callback(self, btn):
if btn == Buttons.BTN_Menu:
self.selection = GameBoard.MAIN_MENU
self.done = True
if btn == Buttons.BTN_B:
self.selection = GameBoard.BUILD_MENU
self.done = True
if btn == Buttons.BTN_Star:
# End the turn
self.selection = GameBoard.END_TURN
self.done = True
self.dice.reset()
for h in self.hexes:
h.set_highlight(False)
if btn == Buttons.BTN_Hash:
# Only roll the dice if not already rolled
if self.dice.total() == 0:
self.dice.roll()
# Highlight the hexes corresponding with the dice roll
num = self.dice.total()
for h in self.hexes:
if h.number['roll'] == num or (num == 7 and h.robber):
h.set_highlight(True)
else:
h.set_highlight(False)
# TODO collect resources
self.redraw = True
class Settlers:
"""A lean mean state machine"""
# Game states
EXIT = 0
MAIN_MENU = 1
TEAM_MENU = 2
GAME = 3
BUILD_MENU = 4
END_TURN_MENU = 5
def __init__(self):
self.state = Settlers.MAIN_MENU
self.game = None
def run(self):
while self.state != Settlers.EXIT:
if self.state == Settlers.MAIN_MENU:
menu = MainMenu(self.game is None)
x = menu.run()
if x == MainMenu.NEW_GAME:
self.state = Settlers.TEAM_MENU
if x == MainMenu.CONTINUE_GAME:
self.state = Settlers.GAME
if x == MainMenu.EXIT:
self.state = Settlers.EXIT
if self.state == Settlers.TEAM_MENU:
menu = TeamMenu()
x = menu.run()
if x == TeamMenu.BACK:
self.state = Settlers.MAIN_MENU
else:
self.game = GameBoard(menu.get_selected_team())
self.state = Settlers.GAME
if self.state == Settlers.GAME:
x = self.game.run()
if x == GameBoard.MAIN_MENU:
self.state = Settlers.MAIN_MENU
if x == GameBoard.BUILD_MENU:
self.state = Settlers.BUILD_MENU
if x == GameBoard.END_TURN:
self.state = Settlers.END_TURN_MENU
if self.state == Settlers.BUILD_MENU:
menu = BuildMenu()
x = menu.run()
if x == BuildMenu.BACK:
self.state = Settlers.GAME
else:
# TODO initiate building a thing
self.state = Settlers.GAME
if self.state == Settlers.END_TURN_MENU:
# TODO: Ask for confirmation
self.state = Settlers.GAME
# User chose exit, a machine reset is the easiest way :-)
restart_to_default()
game = Settlers()
game.run()

BIN
settlers_of_emf/title.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

153
settlers_of_emf/title.svg Normal file
View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8"
sodipodi:docname="title.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
inkscape:export-filename="/home/mbooth/eclipse-workspace/Mk4-Apps/settlers_game/title.svg.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.62"
inkscape:cx="143.29493"
inkscape:cy="689.03226"
inkscape:document-units="mm"
inkscape:current-layer="layer2"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="BG"
inkscape:groupmode="layer"
id="layer1">
<rect
id="rect10"
width="208.64285"
height="101.29762"
x="0.75595242"
y="2.9345238"
style="stroke-width:0.26458332" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Text">
<image
y="10.664136"
x="16.271868"
id="image21"
xlink:href="data:image/png;base64,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
QmCC
"
style="image-rendering:optimizeSpeed"
preserveAspectRatio="none"
height="66.939583"
width="24.606251" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="47.795704"
y="34.977154"
id="text26"><tspan
sodipodi:role="line"
id="tspan24"
x="47.795704"
y="34.977154"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:25.39999962px;font-family:'Source Han Serif CN';-inkscape-font-specification:'Source Han Serif CN Bold';fill:#ffffff;stroke-width:0.26458332">SETTLERS</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="83.304222"
y="70.846184"
id="text26-0"><tspan
sodipodi:role="line"
id="tspan24-2"
x="83.304222"
y="70.846184"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:25.39999962px;font-family:'Source Han Serif CN';-inkscape-font-specification:'Source Han Serif CN Bold';fill:#ffffff;stroke-width:0.26458332">EMF</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.88926697px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.14723167"
x="103.27212"
y="48.41703"
id="text26-0-7"><tspan
sodipodi:role="line"
id="tspan24-2-6"
x="103.27212"
y="48.41703"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:14.1342411px;font-family:'Source Han Serif CN';-inkscape-font-specification:'Source Han Serif CN Bold';fill:#ffffff;stroke-width:0.14723167">OF</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB