diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..10700cc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG] " +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '...' +3. Scroll down to '...' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** +- OS: [e.g. Linux Arch] +- Kernel version [e.g. 6.10.10-arch1-1] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..32e65d1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Bug Report + url: https://github.com/fam007e/TacticsBoard/issues/new?template=bug_report.md + about: File a bug report for the project. + - name: Feature Request + url: https://github.com/fam007e/TacticsBoard/issues/new?template=feature_request.md + about: Suggest a new feature for the project. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..e0f8c70 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE] " +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex: I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..4abae5e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,23 @@ +## Description + +Please include a summary of the changes and the related issue. Please also include relevant motivation and context. + +Fixes # (issue) + +## Type of change + +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Documentation update + +## Checklist: + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] Any dependent changes have been merged and published in downstream modules diff --git a/.github/workflows/build-tact-board.yml b/.github/workflows/build-tact-board.yml new file mode 100644 index 0000000..1346d8f --- /dev/null +++ b/.github/workflows/build-tact-board.yml @@ -0,0 +1,35 @@ +name: C/C++ CI + +on: + push: + branches: [ "main" ] + +jobs: + build-and-release: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: | + /var/cache/apt/archives + key: ${{ runner.os }}-apt-${{ hashFiles('**/Makefile') }} + restore-keys: | + ${{ runner.os }}-apt- + + - name: Install dependencies + run: sudo apt-get update && sudo apt-get upgrade -y && sudo apt install -y build-essential libconfig-dev libdbus-1-dev libegl-dev libev-dev libgl-dev libepoxy-dev libpcre2-dev libpixman-1-dev libx11-xcb-dev libxcb1-dev libxcb-composite0-dev libxcb-damage0-dev libxcb-dpms0-dev libxcb-glx0-dev libxcb-image0-dev libxcb-present-dev libxcb-randr0-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-shape0-dev libxcb-util-dev libxcb-xfixes0-dev libxext-dev meson ninja-build uthash-dev cmake libxft-dev libimlib2-dev libxinerama-dev libxcb-res0-dev + + - name: Clean Previous Build + run: make clean + + - name: Build + run: make + continue-on-error: true + + - name: Check Build Failure + if: ${{ failure() }} + run: echo "Build failed. Investigate the logs for details." && exit 1 diff --git a/.github/workflows/discord-webhook.yml b/.github/workflows/discord-webhook.yml new file mode 100644 index 0000000..74e1521 --- /dev/null +++ b/.github/workflows/discord-webhook.yml @@ -0,0 +1,80 @@ +name: Discord Webhook Notification + +on: + push: + branches: + - main + pull_request: + types: [opened, closed, reopened] + pull_request_review: + types: [submitted, edited, dismissed] + issues: + types: [opened, closed, reopened] + release: + types: [published, edited, released] + check_run: + types: [completed] + check_suite: + types: [completed] + deployment: + types: [created, completed] + +jobs: + notify: + runs-on: ubuntu-latest + steps: + - name: Send notification to Discord for Push + if: github.event_name == 'push' + run: | + curl -H "Content-Type: application/json" \ + -d "{\"content\": \"New push event in repository: ${{ github.repository }} by ${{ github.actor }}.\"}" \ + ${{ secrets.DISCORD_WEBHOOK_URL }} + + - name: Send notification to Discord for Pull Request + if: github.event_name == 'pull_request' + run: | + curl -H "Content-Type: application/json" \ + -d "{\"content\": \"New pull request event in repository: ${{ github.repository }} by ${{ github.actor }}.\"}" \ + ${{ secrets.DISCORD_WEBHOOK_URL }} + + - name: Send notification to Discord for Pull Request Review + if: github.event_name == 'pull_request_review' + run: | + curl -H "Content-Type: application/json" \ + -d "{\"content\": \"New pull request review event in repository: ${{ github.repository }} by ${{ github.actor }}.\"}" \ + ${{ secrets.DISCORD_WEBHOOK_URL }} + + - name: Send notification to Discord for Issues + if: github.event_name == 'issues' + run: | + curl -H "Content-Type: application/json" \ + -d "{\"content\": \"New issue event in repository: ${{ github.repository }} by ${{ github.actor }}.\"}" \ + ${{ secrets.DISCORD_WEBHOOK_URL }} + + - name: Send notification to Discord for Release + if: github.event_name == 'release' + run: | + curl -H "Content-Type: application/json" \ + -d "{\"content\": \"New release event in repository: ${{ github.repository }} by ${{ github.actor }}.\"}" \ + ${{ secrets.DISCORD_WEBHOOK_URL }} + + - name: Send notification to Discord for Check Run + if: github.event_name == 'check_run' + run: | + curl -H "Content-Type: application/json" \ + -d "{\"content\": \"New check run completed in repository: ${{ github.repository }}.\"}" \ + ${{ secrets.DISCORD_WEBHOOK_URL }} + + - name: Send notification to Discord for Check Suite + if: github.event_name == 'check_suite' + run: | + curl -H "Content-Type: application/json" \ + -d "{\"content\": \"New check suite completed in repository: ${{ github.repository }}.\"}" \ + ${{ secrets.DISCORD_WEBHOOK_URL }} + + - name: Send notification to Discord for Deployment + if: github.event_name == 'deployment' + run: | + curl -H "Content-Type: application/json" \ + -d "{\"content\": \"New deployment event in repository: ${{ github.repository }}.\"}" \ + ${{ secrets.DISCORD_WEBHOOK_URL }} diff --git a/.github/workflows/issue-slash-cmd.yml b/.github/workflows/issue-slash-cmd.yml new file mode 100644 index 0000000..dc8f35a --- /dev/null +++ b/.github/workflows/issue-slash-cmd.yml @@ -0,0 +1,73 @@ +name: Close issue on /close + +on: + issue_comment: + types: [created, edited] + +jobs: + closeIssueOnClose: + # Skip this job if the comment was created/edited on a PR + if: ${{ !github.event.issue.pull_request }} + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: none + contents: read + + steps: + - run: echo "command=false" >> $GITHUB_ENV + + - name: Check for /close command + id: check_close_command + run: | + if [[ "${{ contains(github.event.comment.body, '/close') }}" == "true" ]]; then + echo "command=true" >> $GITHUB_ENV + echo "close_command=true" >> $GITHUB_ENV + echo "reopen_command=false" >> $GITHUB_ENV + else + echo "close_command=false" >> $GITHUB_ENV + fi + + - name: Check for /open or /reopen command + id: check_reopen_command + run: | + if [[ "${{ contains(github.event.comment.body, '/open') }}" == "true" ]] || [[ "${{ contains(github.event.comment.body, '/reopen') }}" == "true" ]]; then + echo "command=true" >> $GITHUB_ENV + echo "reopen_command=true" >> $GITHUB_ENV + echo "close_command=false" >> $GITHUB_ENV + else + echo "reopen_command=false" >> $GITHUB_ENV + fi + + - name: Check if the user is allowed + id: check_user + if: env.command == 'true' + run: | + ALLOWED_USERS=("fam007e") + if [[ " ${ALLOWED_USERS[@]} " =~ " ${{ github.event.comment.user.login }} " ]]; then + echo "user=true" >> $GITHUB_ENV + else + echo "user=false" >> $GITHUB_ENV + fi + + - name: Close issue if conditions are met + if: env.close_command == 'true' && env.user == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + run: | + echo Closing the issue... + if [[ "${{ contains(github.event.comment.body, 'not planned') }}" == "true" ]]; then + gh issue close $ISSUE_NUMBER --repo ${{ github.repository }} --reason 'not planned' + else + gh issue close $ISSUE_NUMBER --repo ${{ github.repository }} + fi + + - name: Reopen issue if conditions are met + if: env.reopen_command == 'true' && env.user == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + run: | + echo Reopening the issue... + gh issue reopen $ISSUE_NUMBER --repo ${{ github.repository }} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a1bad51 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.10) +project(TacticsBoard) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +include_directories(include) + +file(GLOB SOURCES "source/*.cpp") +add_executable(tactics_board ${SOURCES}) + +find_package(SFML 2.5 COMPONENTS graphics window system REQUIRED) +target_link_libraries(tactics_board sfml-graphics sfml-window sfml-system) diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7985a3 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# TacticsBoard +Football Tactics Board diff --git a/include/Menu.hpp b/include/Menu.hpp new file mode 100644 index 0000000..a0e1301 --- /dev/null +++ b/include/Menu.hpp @@ -0,0 +1,28 @@ +#ifndef MENU_HPP +#define MENU_HPP + +#include +#include +#include + +class Menu { +public: + struct Icon { + sf::Texture texture; + sf::Sprite sprite; + sf::Sprite shadow; // Add shadow sprite + }; + + Menu(); + void initialize(const sf::Vector2u& windowSize); + void handleMouseClick(const sf::Vector2i& mousePos, bool& menuVisible, bool& dragging, int& selectedTool); + void draw(sf::RenderWindow& window, bool menuVisible); + bool isPointInSprite(const sf::Sprite& sprite, const sf::Vector2i& point); + +private: + std::vector icons; + void initializeIcon(Icon& icon, const std::string& filepath, float scale, const sf::Vector2f& position); + void generateShadow(Icon& icon); // Declare the shadow generation method +}; + +#endif // MENU_HPP diff --git a/include/Player.hpp b/include/Player.hpp new file mode 100644 index 0000000..d49d2dd --- /dev/null +++ b/include/Player.hpp @@ -0,0 +1,25 @@ +#ifndef PLAYER_HPP +#define PLAYER_HPP + +#include +#include + +class Player { +public: + Player(const sf::Vector2f& position, const std::string& name, int number); + + void draw(sf::RenderWindow& window); + void setPosition(const sf::Vector2f& position); + bool contains(const sf::Vector2i& point) const; + const sf::Vector2f& getPosition() const; + +private: + sf::CircleShape shape; + sf::Text nameText; + sf::Text numberText; + sf::Font font; + std::string name; + int number; +}; + +#endif // PLAYER_HPP diff --git a/include/TacticsBoard.hpp b/include/TacticsBoard.hpp new file mode 100644 index 0000000..1f8e8a4 --- /dev/null +++ b/include/TacticsBoard.hpp @@ -0,0 +1,32 @@ +#ifndef TACTICSBOARD_HPP +#define TACTICSBOARD_HPP + +#include +#include +#include "Player.hpp" +#include "Menu.hpp" + +class TacticsBoard { +public: + TacticsBoard(); + void run(); + +private: + sf::Texture pitchTexture; + sf::Sprite pitchSprite; + + std::vector players; + bool menuVisible; + bool dragging; + Player* selectedPlayer; + int selectedTool; + sf::Vector2f oldMousePosition; + + Menu menu; + + void handleMouseEvent(sf::Event& event, sf::RenderWindow& window); + void update(); + void render(sf::RenderWindow& window); +}; + +#endif // TACTICSBOARD_HPP diff --git a/resources/fonts/FiraCodeNerdFont-Bold.ttf b/resources/fonts/FiraCodeNerdFont-Bold.ttf new file mode 100644 index 0000000..8272834 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFont-Bold.ttf differ diff --git a/resources/fonts/FiraCodeNerdFont-Light.ttf b/resources/fonts/FiraCodeNerdFont-Light.ttf new file mode 100644 index 0000000..210aba9 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFont-Light.ttf differ diff --git a/resources/fonts/FiraCodeNerdFont-Medium.ttf b/resources/fonts/FiraCodeNerdFont-Medium.ttf new file mode 100644 index 0000000..88f657f Binary files /dev/null and b/resources/fonts/FiraCodeNerdFont-Medium.ttf differ diff --git a/resources/fonts/FiraCodeNerdFont-Regular.ttf b/resources/fonts/FiraCodeNerdFont-Regular.ttf new file mode 100644 index 0000000..c530364 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFont-Regular.ttf differ diff --git a/resources/fonts/FiraCodeNerdFont-Retina.ttf b/resources/fonts/FiraCodeNerdFont-Retina.ttf new file mode 100644 index 0000000..ea2d5e1 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFont-Retina.ttf differ diff --git a/resources/fonts/FiraCodeNerdFont-SemiBold.ttf b/resources/fonts/FiraCodeNerdFont-SemiBold.ttf new file mode 100644 index 0000000..fa4dbea Binary files /dev/null and b/resources/fonts/FiraCodeNerdFont-SemiBold.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontMono-Bold.ttf b/resources/fonts/FiraCodeNerdFontMono-Bold.ttf new file mode 100644 index 0000000..ddc143e Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontMono-Bold.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontMono-Light.ttf b/resources/fonts/FiraCodeNerdFontMono-Light.ttf new file mode 100644 index 0000000..606f2c3 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontMono-Light.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontMono-Medium.ttf b/resources/fonts/FiraCodeNerdFontMono-Medium.ttf new file mode 100644 index 0000000..e0df7b7 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontMono-Medium.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontMono-Regular.ttf b/resources/fonts/FiraCodeNerdFontMono-Regular.ttf new file mode 100644 index 0000000..fd34efd Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontMono-Regular.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontMono-Retina.ttf b/resources/fonts/FiraCodeNerdFontMono-Retina.ttf new file mode 100644 index 0000000..8b808b1 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontMono-Retina.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontMono-SemiBold.ttf b/resources/fonts/FiraCodeNerdFontMono-SemiBold.ttf new file mode 100644 index 0000000..6660bbe Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontMono-SemiBold.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontPropo-Bold.ttf b/resources/fonts/FiraCodeNerdFontPropo-Bold.ttf new file mode 100644 index 0000000..356a531 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontPropo-Bold.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontPropo-Light.ttf b/resources/fonts/FiraCodeNerdFontPropo-Light.ttf new file mode 100644 index 0000000..bf85d40 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontPropo-Light.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontPropo-Medium.ttf b/resources/fonts/FiraCodeNerdFontPropo-Medium.ttf new file mode 100644 index 0000000..ea41616 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontPropo-Medium.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontPropo-Regular.ttf b/resources/fonts/FiraCodeNerdFontPropo-Regular.ttf new file mode 100644 index 0000000..06e77e3 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontPropo-Regular.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontPropo-Retina.ttf b/resources/fonts/FiraCodeNerdFontPropo-Retina.ttf new file mode 100644 index 0000000..a169716 Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontPropo-Retina.ttf differ diff --git a/resources/fonts/FiraCodeNerdFontPropo-SemiBold.ttf b/resources/fonts/FiraCodeNerdFontPropo-SemiBold.ttf new file mode 100644 index 0000000..fecdadd Binary files /dev/null and b/resources/fonts/FiraCodeNerdFontPropo-SemiBold.ttf differ diff --git a/resources/fonts/LICENSE b/resources/fonts/LICENSE new file mode 100644 index 0000000..805e0b3 --- /dev/null +++ b/resources/fonts/LICENSE @@ -0,0 +1,93 @@ +Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/resources/fonts/README.md b/resources/fonts/README.md new file mode 100644 index 0000000..d50c2f3 --- /dev/null +++ b/resources/fonts/README.md @@ -0,0 +1,48 @@ +# Nerd Fonts + +This is an archived font from the Nerd Fonts release v3.2.1. + +For more information see: +* https://github.com/ryanoasis/nerd-fonts/ +* https://github.com/ryanoasis/nerd-fonts/releases/latest/ + +# Fira Code + +**Fira Code** is a free monospaced font with programming ligatures. + +For more information have a look at the upstream website: https://github.com/tonsky/FiraCode + +Version: 6.2 + +## Which font? + +### TL;DR + +* Pick your font family: + * If you are limited to monospaced fonts (because of your terminal, etc) then pick a font with `Nerd Font Mono` (or `NFM`). + * If you want to have bigger icons (usually around 1.5 normal letters wide) pick a font without `Mono` i.e. `Nerd Font` (or `NF`). Most terminals support this, but ymmv. + * If you work in a proportional context (GUI elements or edit a presentation etc) pick a font with `Nerd Font Propo` (or `NFP`). + +### Ligatures + +Ligatures are generally preserved in the patched fonts. +Nerd Fonts `v2.0.0` had no ligatures in the `Nerd Font Mono` fonts, this has been dropped with `v2.1.0`. +If you have a ligature-aware terminal and don't want ligatures you can (usually) disable them in the terminal settings. + +### Explanation + +Once you narrow down your font choice of family (`Droid Sans`, `Inconsolata`, etc) and style (`bold`, `italic`, etc) you have 2 main choices: + +#### `Option 1: Download already patched font` + + * For a stable version download a font package from the [release page](https://github.com/ryanoasis/nerd-fonts/releases) + * Or download the development version from the folders here + +#### `Option 2: Patch your own font` + + * Patch your own variations with the various options provided by the font patcher (i.e. not include all symbols for smaller font size) + +For more information see: [The FAQ](https://github.com/ryanoasis/nerd-fonts/wiki/FAQ-and-Troubleshooting#which-font) + +[SIL-RFN]:http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web_fonts_and_RFNs#14cbfd4a + diff --git a/resources/fonts/default.ttf b/resources/fonts/default.ttf new file mode 100644 index 0000000..e0df7b7 Binary files /dev/null and b/resources/fonts/default.ttf differ diff --git a/resources/icons/arrow_icon.png b/resources/icons/arrow_icon.png new file mode 100644 index 0000000..023844a Binary files /dev/null and b/resources/icons/arrow_icon.png differ diff --git a/resources/icons/close_icon.png b/resources/icons/close_icon.png new file mode 100644 index 0000000..2c39a2e Binary files /dev/null and b/resources/icons/close_icon.png differ diff --git a/resources/icons/eraser_icon.png b/resources/icons/eraser_icon.png new file mode 100644 index 0000000..a3072bf Binary files /dev/null and b/resources/icons/eraser_icon.png differ diff --git a/resources/icons/football.png b/resources/icons/football.png new file mode 100644 index 0000000..e8cb8f2 Binary files /dev/null and b/resources/icons/football.png differ diff --git a/resources/icons/line_icon.png b/resources/icons/line_icon.png new file mode 100644 index 0000000..2e57276 Binary files /dev/null and b/resources/icons/line_icon.png differ diff --git a/resources/icons/menu_icon.png b/resources/icons/menu_icon.png new file mode 100644 index 0000000..44e39c4 Binary files /dev/null and b/resources/icons/menu_icon.png differ diff --git a/resources/icons/oval_icon.png b/resources/icons/oval_icon.png new file mode 100644 index 0000000..392250f Binary files /dev/null and b/resources/icons/oval_icon.png differ diff --git a/resources/icons/playerlist_icon.png b/resources/icons/playerlist_icon.png new file mode 100644 index 0000000..d8a5835 Binary files /dev/null and b/resources/icons/playerlist_icon.png differ diff --git a/resources/icons/rectangle_icon.png b/resources/icons/rectangle_icon.png new file mode 100644 index 0000000..dd718ba Binary files /dev/null and b/resources/icons/rectangle_icon.png differ diff --git a/resources/pitches/football_pitch.png b/resources/pitches/football_pitch.png new file mode 100644 index 0000000..11e5e3f Binary files /dev/null and b/resources/pitches/football_pitch.png differ diff --git a/resources/pitches/football_pitch_v0.jpg b/resources/pitches/football_pitch_v0.jpg new file mode 100644 index 0000000..1518c7b Binary files /dev/null and b/resources/pitches/football_pitch_v0.jpg differ diff --git a/resources/player_list/players.txt b/resources/player_list/players.txt new file mode 100644 index 0000000..f97cdca --- /dev/null +++ b/resources/player_list/players.txt @@ -0,0 +1,5 @@ +Team A, Blue +Player 1, Player 2, Player 3, Player 4, Player 5, Player 6, Player 7, Player 8, Player 9, Player 10, Player 11 + +Team B, Red +Player 1, Player 2, Player 3, Player 4, Player 5, Player 6, Player 7, Player 8, Player 9, Player 10, Player 11 diff --git a/source/Menu.cpp b/source/Menu.cpp new file mode 100644 index 0000000..a106284 --- /dev/null +++ b/source/Menu.cpp @@ -0,0 +1,112 @@ +#include "Menu.hpp" +#include + +Menu::Menu() { + // Preload icons with file paths + std::vector iconFilepaths = { + "../resources/icons/menu_icon.png", + "../resources/icons/close_icon.png", + "../resources/icons/arrow_icon.png", + "../resources/icons/line_icon.png", + "../resources/icons/eraser_icon.png", + "../resources/icons/oval_icon.png", + "../resources/icons/rectangle_icon.png", + "../resources/icons/playerlist_icon.png" + }; + + // Load textures and create sprites + for (const auto& filepath : iconFilepaths) { + Icon icon; + if (!icon.texture.loadFromFile(filepath)) { + std::cerr << "Error loading texture: " << filepath << std::endl; + } + icon.sprite.setTexture(icon.texture); + icons.push_back(icon); + } +} + +void Menu::generateShadow(Icon& icon) { + // Create a new image for the shadow based on the original texture + sf::Image image = icon.texture.copyToImage(); + sf::Image shadowImage; + shadowImage.create(image.getSize().x, image.getSize().y, sf::Color(0, 0, 0, 0)); + + // Process each pixel to create a shadow + for (unsigned y = 0; y < image.getSize().y; ++y) { + for (unsigned x = 0; x < image.getSize().x; ++x) { + sf::Color color = image.getPixel(x, y); + if (color.a > 0) { // Only shadow non-transparent pixels + shadowImage.setPixel(x, y, sf::Color(0, 0, 0, 100)); // Semi-transparent black + } + } + } + + // Load the modified image into the shadow texture + sf::Texture shadowTexture; + shadowTexture.loadFromImage(shadowImage); + icon.shadow.setTexture(shadowTexture); + icon.shadow.setScale(icon.sprite.getScale()); + icon.shadow.setPosition(icon.sprite.getPosition() + sf::Vector2f(5, 5)); // Offset position for shadow +} + +void Menu::initialize(const sf::Vector2u& windowSize) { + // Define sizes and positions + float menuIconSize = windowSize.x * 0.045f; + float closeIconSize = windowSize.x * 0.036f; + float toolIconSize = windowSize.x * 0.02f; + float startY = 0.12f * windowSize.y; + float spacing = toolIconSize * 1.2f; + + // Initialize icons with scales and positions + initializeIcon(icons[0], "../resources/icons/menu_icon.png", menuIconSize, {0.02f * windowSize.x, 0.02f * windowSize.y}); + initializeIcon(icons[1], "../resources/icons/close_icon.png", closeIconSize, {0.98f * windowSize.x - icons[1].sprite.getGlobalBounds().width, 0.02f * windowSize.y}); + + for (size_t i = 2; i < icons.size(); ++i) { + initializeIcon(icons[i], "", toolIconSize, {0.02f * windowSize.x, startY + (i - 2) * spacing}); + } +} + +void Menu::initializeIcon(Icon& icon, const std::string& filepath, float scale, const sf::Vector2f& position) { + if (!filepath.empty()) { + icon.texture.loadFromFile(filepath); + } + icon.sprite.setScale(scale / icon.texture.getSize().x, scale / icon.texture.getSize().y); + icon.sprite.setPosition(position); + generateShadow(icon); // Generate shadow for the icon +} + +void Menu::handleMouseClick(const sf::Vector2i& mousePos, bool& menuVisible, bool& dragging, int& selectedTool) { + if (isPointInSprite(icons[0].sprite, mousePos)) { + menuVisible = !menuVisible; + std::cout << "Menu icon clicked" << std::endl; + } + + if (menuVisible) { + for (size_t i = 2; i < icons.size(); ++i) { + if (isPointInSprite(icons[i].sprite, mousePos)) { + selectedTool = static_cast(i - 2); + break; + } + } + } +} + +void Menu::draw(sf::RenderWindow& window, bool menuVisible) { + // Always draw the menu and close icons + window.draw(icons[0].shadow); + window.draw(icons[0].sprite); + window.draw(icons[1].shadow); + window.draw(icons[1].sprite); + + if (menuVisible) { + for (size_t i = 2; i < icons.size(); ++i) { + window.draw(icons[i].shadow); + window.draw(icons[i].sprite); + } + } +} + +bool Menu::isPointInSprite(const sf::Sprite& sprite, const sf::Vector2i& point) { + sf::FloatRect bounds = sprite.getGlobalBounds(); + return bounds.contains(static_cast(point)); +} diff --git a/source/Player.cpp b/source/Player.cpp new file mode 100644 index 0000000..30ca281 --- /dev/null +++ b/source/Player.cpp @@ -0,0 +1,44 @@ +#include "Player.hpp" + +Player::Player(const sf::Vector2f& position, const std::string& name, int number) + : name(name), number(number) { + shape.setRadius(10.0f); + shape.setFillColor(sf::Color::Blue); + shape.setPosition(position); + + if (!font.loadFromFile("../resources/fonts/arial.ttf")) { + // Handle error + } + + nameText.setFont(font); + nameText.setString(name); + nameText.setCharacterSize(12); + nameText.setFillColor(sf::Color::White); + nameText.setPosition(position.x, position.y - 20); + + numberText.setFont(font); + numberText.setString(std::to_string(number)); + numberText.setCharacterSize(12); + numberText.setFillColor(sf::Color::White); + numberText.setPosition(position.x, position.y + 20); +} + +void Player::draw(sf::RenderWindow& window) { + window.draw(shape); + window.draw(nameText); + window.draw(numberText); +} + +void Player::setPosition(const sf::Vector2f& position) { + shape.setPosition(position); + nameText.setPosition(position.x, position.y - 20); + numberText.setPosition(position.x, position.y + 20); +} + +bool Player::contains(const sf::Vector2i& point) const { + return shape.getGlobalBounds().contains(static_cast(point)); +} + +const sf::Vector2f& Player::getPosition() const { + return shape.getPosition(); +} diff --git a/source/TacticsBoard.cpp b/source/TacticsBoard.cpp new file mode 100644 index 0000000..a42cb64 --- /dev/null +++ b/source/TacticsBoard.cpp @@ -0,0 +1,127 @@ +// TacticsBoard.cpp +#include "TacticsBoard.hpp" +#include "Player.hpp" +#include +#include + +TacticsBoard::TacticsBoard() + : menuVisible(false), dragging(false), selectedPlayer(nullptr), selectedTool(-1) { + // Get the current desktop resolution + sf::VideoMode desktop = sf::VideoMode::getDesktopMode(); + unsigned int windowWidth = desktop.width; + unsigned int windowHeight = desktop.height; + + if (!pitchTexture.loadFromFile("../resources/pitches/football_pitch.png")) { + std::cerr << "Error loading football pitch texture" << std::endl; + } + pitchSprite.setTexture(pitchTexture); + pitchSprite.setScale( + float(windowWidth) / pitchTexture.getSize().x, + float(windowHeight) / pitchTexture.getSize().y + ); + + menu.initialize(sf::Vector2u(windowWidth, windowHeight)); + + // Initialize players + std::vector playerNames = {"Player1", "Player2", "Player3", "Player4", "Player5", "Player6", "Player7", "Player8", "Player9", "Player10", "Player11"}; + int numPlayersPerTeam = 11; // Assuming 11 players per team + float fieldWidth = windowWidth; // Width of the window + float fieldHeight = windowHeight; // Height of the window + float playerRadius = 10.0f; // Radius of each player circle + + // Initialize team 1 players (left side) + float startX = fieldWidth * 0.1f; // Start position from left edge + float startY = fieldHeight / 2 - (numPlayersPerTeam / 2 * playerRadius * 2); // Center vertically + + for (int i = 0; i < numPlayersPerTeam; ++i) { + players.emplace_back(sf::Vector2f(startX + i * (playerRadius * 2), startY + i * (playerRadius * 2)), playerNames[i], i + 1); + } + + // Initialize team 2 players (right side) + startX = fieldWidth * 0.9f; // Start position from right edge + startY = fieldHeight / 2 - (numPlayersPerTeam / 2 * playerRadius * 2); // Center vertically + + for (int i = 0; i < numPlayersPerTeam; ++i) { + players.emplace_back(sf::Vector2f(startX - i * (playerRadius * 2), startY + i * (playerRadius * 2)), playerNames[i], i + 1); + } +} + +void TacticsBoard::run() { + // Get the current desktop resolution + sf::VideoMode desktop = sf::VideoMode::getDesktopMode(); + unsigned int windowWidth = desktop.width; + unsigned int windowHeight = desktop.height; + + sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "Tactics Board", sf::Style::Fullscreen); + while (window.isOpen()) { + sf::Event event; + while (window.pollEvent(event)) { + if (event.type == sf::Event::Closed) + window.close(); + handleMouseEvent(event, window); + } + + update(); + + window.clear(); + render(window); + window.display(); + } +} + +void TacticsBoard::handleMouseEvent(sf::Event& event, sf::RenderWindow& window) { + if (event.type == sf::Event::MouseButtonPressed) { + if (event.mouseButton.button == sf::Mouse::Left) { + sf::Vector2i mousePos = sf::Mouse::getPosition(window); + menu.handleMouseClick(mousePos, menuVisible, dragging, selectedTool); + if (!menuVisible) { + for (auto& player : players) { + if (player.contains(mousePos)) { + dragging = true; + selectedPlayer = &player; + oldMousePosition = window.mapPixelToCoords(mousePos); + break; + } + } + } + } + } else if (event.type == sf::Event::MouseButtonReleased) { + if (event.mouseButton.button == sf::Mouse::Left) { + dragging = false; + selectedPlayer = nullptr; + } + } else if (event.type == sf::Event::MouseMoved) { + if (dragging && selectedPlayer) { + sf::Vector2i mousePos = sf::Mouse::getPosition(window); + sf::Vector2f newMousePosition = window.mapPixelToCoords(mousePos); + sf::Vector2f offset = newMousePosition - oldMousePosition; + sf::Vector2f newPosition = selectedPlayer->getPosition() + offset; + + // Check for overlap with other players + bool overlap = false; + for (const auto& player : players) { + if (&player != selectedPlayer && player.contains(sf::Vector2i(newPosition))) { + overlap = true; + break; + } + } + + if (!overlap) { + selectedPlayer->setPosition(newPosition); + oldMousePosition = newMousePosition; + } + } + } +} + +void TacticsBoard::update() { + // Update logic if needed +} + +void TacticsBoard::render(sf::RenderWindow& window) { + window.draw(pitchSprite); + menu.draw(window, menuVisible); + + for (auto& player : players) { + player.draw(window); + }} \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..f8568d0 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,7 @@ +#include "TacticsBoard.hpp" + +int main() { + TacticsBoard board; + board.run(); + return 0; +}