+++ /dev/null
-# Auto detect text files and perform LF normalization
-* text=auto
+++ /dev/null
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-share/python-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-MANIFEST
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.nox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-*.py,cover
-.hypothesis/
-.pytest_cache/
-cover/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-db.sqlite3-journal
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-.pybuilder/
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
-# pyenv
-# For a library or package, you might want to ignore these files since the code is
-# intended to run in multiple environments; otherwise, check them in:
-# .python-version
-
-# pipenv
-# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-# However, in case of collaboration, if having platform-specific dependencies or dependencies
-# having no cross-platform support, pipenv may install dependencies that don't work, or not
-# install all needed dependencies.
-#Pipfile.lock
-
-# poetry
-# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
-# This is especially recommended for binary packages to ensure reproducibility, and is more
-# commonly ignored for libraries.
-# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
-#poetry.lock
-
-# PEP 582; used by e.g. github.com/David-OConnor/pyflow
-__pypackages__/
-
-# Celery stuff
-celerybeat-schedule
-celerybeat.pid
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-.dmypy.json
-dmypy.json
-
-# Pyre type checker
-.pyre/
-
-# pytype static type analyzer
-.pytype/
-
-# Cython debug symbols
-cython_debug/
-
-# PyCharm
-# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
-# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
-# and can be added to the global gitignore or merged into this file. For a more nuclear
-# option (not recommended) you can uncomment the following to ignore the entire idea folder.
-.idea/
--- /dev/null
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PYTHON_MODULE" version="4">
+ <component name="NewModuleRootManager">
+ <content url="file://$MODULE_DIR$" />
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
\ No newline at end of file
--- /dev/null
+<component name="InspectionProjectProfileManager">
+ <settings>
+ <option name="USE_PROJECT_PROFILE" value="false" />
+ <version value="1.0" />
+ </settings>
+</component>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/.idea/fruit-ninja.iml" filepath="$PROJECT_DIR$/.idea/fruit-ninja.iml" />
+ </modules>
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="" vcs="Git" />
+ </component>
+</project>
\ No newline at end of file
+++ /dev/null
-MIT License
-
-Copyright (c) 2023 Skullheadx
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+++ /dev/null
-from fruit import Fruit
-from setup import *
-
-
-class Bomb(Fruit):
- RADIUS = 55
-
- EXPLOSION_RADIUS = 100
- POWER = 75
-
- def __init__(self):
- super().__init__()
- self.radius = self.RADIUS
- self.exploded = False
- self.exploded_time = 0
-
- def update(self, delta):
- super().update(delta)
- if self.exploded:
- self.exploded_time += delta ** 2
-
- def explode(self, fruits, bombs, effects):
- if self in bombs:
- self.exploded = True
- self.velocity = pygame.Vector2(0, 0)
- self.acceleration = pygame.Vector2(0, 0)
-
- for fruit in fruits:
- fruit.velocity += (fruit.position - self.position).normalize() * self.POWER
- for effect in effects:
- effect.velocity += (effect.position - self.position).normalize() * self.POWER
- for bomb in bombs:
- if not bomb.exploded:
- bomb.explode(fruits, bombs, effects)
-
- def draw(self, surf):
- if self.exploded:
- pygame.draw.circle(surf, DARK_RED, self.position,
- clamp(self.RADIUS + self.exploded_time / 1000 * 100, 0, 300))
- pygame.draw.circle(surf, BLACK, self.position, clamp(self.RADIUS + self.exploded_time / 1000 * 100, 0, 300),
- self.OUTLINE_WIDTH)
- else:
- pygame.draw.circle(surf, BLACK, self.position, self.RADIUS)
+++ /dev/null
-from setup import *
-
-
-class ComboCounter:
- LIFE_TIME = 1000
-
- def __init__(self, position, combo):
- self.position = pygame.Vector2(position)
- self.velocity = pygame.Vector2(0, -100)
- self.time = self.LIFE_TIME
- self.combo = combo
- self.text_surface = font_large.render(self.combo, True, BLACK)
-
- def update(self, delta):
- self.position += self.velocity * delta / 1000
- self.time -= delta
- if self.time <= 0:
- return True
-
- def draw(self, surf):
- surf.blit(self.text_surface, self.position)
+++ /dev/null
-from setup import *
-
-
-class Effect:
- LIFE_TIME = 750
- SPEED_RANGE = [250, 350]
- RADIUS_RANGE = [0.25, 0.75]
-
- OUTLINE_WIDTH = 1
-
- def __init__(self, position, radius, color, darken=True):
- self.position = pygame.Vector2(position)
- self.velocity = pygame.Vector2(random.random() - 0.5, random.random() - 0.5).normalize() * lerp(
- self.SPEED_RANGE[0], self.SPEED_RANGE[1], random.random())
- self.radius = radius * lerp(self.RADIUS_RANGE[0], self.RADIUS_RANGE[1], random.random())
- self.time = self.LIFE_TIME
- if darken:
- self.color = DARKEN[color]
- self.outline_color = color
- else:
- self.color = color
- self.outline_color = DARKEN[color]
-
- def update(self, delta):
- self.position += self.velocity * delta / 1000
- self.time -= delta
- if self.time <= 0:
- return True
-
- def draw(self, surf):
- pygame.draw.circle(surf, self.color, self.position, self.radius)
- pygame.draw.circle(surf, self.outline_color, self.position, self.radius, self.OUTLINE_WIDTH)
+++ /dev/null
-from setup import *
-
-
-class Fruit:
- RADIUS_RANGE = [35, 65] # [25, 50]
-
- HORIZONTAL_SPAWN_RANGE = [max(RADIUS_RANGE), WIDTH - max(RADIUS_RANGE)]
- VERTICAL_SPAWN_RANGE = [HEIGHT + max(RADIUS_RANGE), HEIGHT * 2 + max(RADIUS_RANGE)]
-
- VERTICAL_TARGET_RANGE = [max(RADIUS_RANGE), HEIGHT * 4 / 5]
- HORIZONTAL_TARGET_RANGE = [WIDTH / 6, WIDTH * 5 / 6]
-
- GRAVITY = 275
-
- COLORS = [RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE]
- OUTLINE_WIDTH = 3
-
- def __init__(self):
- self.radius = lerp(self.RADIUS_RANGE[0], self.RADIUS_RANGE[1], random.random())
-
- self.target = pygame.Vector2(
- lerp(self.HORIZONTAL_TARGET_RANGE[0], self.HORIZONTAL_TARGET_RANGE[1], random.random()),
- lerp(self.VERTICAL_TARGET_RANGE[0], self.VERTICAL_TARGET_RANGE[1], random.random()))
- self.position = pygame.Vector2(
- lerp(self.HORIZONTAL_SPAWN_RANGE[0], self.HORIZONTAL_SPAWN_RANGE[1], random.random()),
- lerp(self.VERTICAL_SPAWN_RANGE[0], self.VERTICAL_SPAWN_RANGE[1], random.random()))
- self.acceleration = pygame.Vector2(0, self.GRAVITY)
-
- # self.previous_position = self.position
-
- dy = self.target.y - self.position.y
- dx = self.target.x - self.position.x
- t = (-2 / self.GRAVITY * dy) ** 0.5
- self.velocity = pygame.Vector2(dx / t, -(-2 * self.GRAVITY * dy) ** 0.5)
-
- self.color = random.choice(self.COLORS)
-
- def update(self, delta):
- # self.previous_position = self.position.copy() - self.velocity / 1000 * 30
- self.velocity += self.acceleration * delta / 1000
- self.position += self.velocity * delta / 1000
-
- def get_rect(self):
- return pygame.Rect(self.position - pygame.Vector2(self.radius / 2, self.radius / 2),
- pygame.Vector2(self.radius, self.radius))
-
- def draw(self, surf):
- # pygame.draw.circle(surf, DARK_GRAY, self.previous_position, self.radius)
-
- pygame.draw.circle(surf, self.color, self.position, self.radius)
- pygame.draw.circle(surf, DARKEN[self.color], self.position, self.radius, self.OUTLINE_WIDTH)
-
- # pygame.draw.circle(surf, BLACK, self.target, 10)
+++ /dev/null
-from bomb import Bomb
-from combo_counter import ComboCounter
-from effect import Effect
-from fruit import Fruit
-from player import Player
-from setup import *
-
-
-class Game:
- BOMB_CHANCE = 0.1
- EFFECT_COUNT_PER_FRUIT = 20
- EFFECT_COUNT_PER_BOMB = 0
- COMBO_TIME = 250
- GAME_OVER_TIME = 2000
-
- def __init__(self):
- self.player = Player()
- self.fruits = [Fruit()]
- self.bombs = []
- self.effects = []
- self.combo_counters = []
- self.wave = 1
- self.score = 0
- self.time_since_last_hit = 0
- self.current_combo = 0
-
- self.game_over = False
- self.game_over_time = 0
-
- def update(self, delta):
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- return COMMAND_EXIT
- if event.type == pygame.KEYUP:
- if event.key == pygame.K_ESCAPE:
- return COMMAND_EXIT
-
- if not self.game_over:
- self.player.update(delta)
- else:
- self.game_over_time += delta
- if self.game_over_time > self.GAME_OVER_TIME:
- return COMMAND_START
- hits = []
- for fruit in self.fruits:
- fruit.update(delta)
- if self.player.hits(fruit):
- hits.append(fruit)
- fr = fruit.get_rect()
- if ((not -fruit.radius < fr.x < WIDTH + fruit.radius) or fr.y > HEIGHT) and fruit.velocity.y > 0:
- self.fruits.remove(fruit)
- self.time_since_last_hit += delta
-
- if self.time_since_last_hit < self.COMBO_TIME:
- self.score += self.current_combo
- else:
- self.current_combo = 0
- for hit in hits:
- for i in range(self.EFFECT_COUNT_PER_FRUIT):
- self.effects.append(Effect(hit.position, hit.radius, hit.color))
- if hit in self.fruits:
- self.fruits.remove(hit)
- self.score += 1
- if self.time_since_last_hit < self.COMBO_TIME:
- self.current_combo += 1
- if self.current_combo > 1:
- self.combo_counters.append(ComboCounter(hit.position, f"x{self.current_combo + 1}"))
-
- self.time_since_last_hit = 0
-
- for effect in self.effects:
- effect_status = effect.update(delta)
- if effect_status:
- self.effects.remove(effect)
-
- for combo in self.combo_counters:
- combo_status = combo.update(delta)
- if combo_status:
- self.combo_counters.remove(combo)
-
- for bomb in self.bombs:
- bomb.update(delta)
- if self.player.hits(bomb):
- for i in range(self.EFFECT_COUNT_PER_BOMB):
- self.effects.append(
- Effect(bomb.position + pygame.Vector2(random.random(), random.random()), bomb.radius, WHITE,
- darken=False))
-
- bomb.explode(self.fruits, self.bombs, self.effects)
- self.game_over = True
- self.player.sliced_points.clear()
- br = bomb.get_rect()
- if ((not -bomb.RADIUS < br.x < WIDTH + bomb.RADIUS) or br.y > HEIGHT) and bomb.velocity.y > 0:
- self.bombs.remove(bomb)
-
- if len(self.fruits) == 0 and len(self.bombs) == 0:
- self.wave += 1
- for i in range(self.wave):
- if random.random() < self.BOMB_CHANCE:
- self.bombs.append(Bomb())
- else:
- self.fruits.append(Fruit())
-
- def draw(self, surf):
- screen.fill(BROWN)
- for effect in self.effects:
- effect.draw(surf)
- for bomb in self.bombs:
- bomb.draw(surf)
- for fruit in self.fruits:
- fruit.draw(surf)
- for combo in self.combo_counters:
- combo.draw(surf)
- self.player.draw(surf)
- text_surf = font.render(f"SCORE {self.score}", True, WHITE)
- surf.blit(text_surf, (0, 0))
- # text_surf2 = font.render(f"COMBO {self.current_combo}", True, WHITE)
- # surf.blit(text_surf2, (0, text_surf.get_height()))
- # text_surf2 = font.render(f"TIME SINCE LAST HIT {round(self.time_since_last_hit / 1000, 1)}", True, BLACK)
- # surf.blit(text_surf2, (WIDTH - text_surf2.get_width(), text_surf.get_height()))
-
- if self.game_over:
- game_over_surf = font_large.render("GAME OVER", True, WHITE)
- surf.blit(game_over_surf, game_over_surf.get_rect(center=(WIDTH / 2, HEIGHT / 2.5)))
+++ /dev/null
-from game import Game
-from setup import *
-
-FPS = 120
-clock = pygame.time.Clock()
-
-scene = Game()
-
-is_running = True
-while is_running:
- delta = clock.tick(FPS)
- status = scene.update(delta)
- scene.draw(screen)
- pygame.display.update()
-
- if status == COMMAND_EXIT:
- is_running = False
- elif status == COMMAND_START:
- scene = Game()
-
-pygame.quit()
+++ /dev/null
-from setup import *
-
-
-class Player:
- LIFE_TIME = 100
- INFLATE_SCALE = 20
-
- def __init__(self):
- self.sliced_points = []
- self.hitboxes = []
-
- def update(self, delta):
- pressed = pygame.mouse.get_pressed()
- if pressed[0]:
- pos = pygame.mouse.get_pos()
- self.sliced_points.append((pygame.Vector2(pos), pygame.time.get_ticks()))
- for i, val in enumerate(self.sliced_points):
- pos, time = val
- if pygame.time.get_ticks() - time > self.LIFE_TIME:
- self.sliced_points.pop(i)
- break
- self.hitboxes.clear()
- if len(self.sliced_points) > 1:
- for i in range(len(self.sliced_points) - 1):
- self.hitboxes.append(pygame.Rect(self.sliced_points[i][0],
- (self.sliced_points[i][0] - self.sliced_points[i + 1][0])).inflate(
- self.INFLATE_SCALE, self.INFLATE_SCALE))
-
- def hits(self, fruit):
- for hitbox in self.hitboxes:
- if hitbox.colliderect(fruit.get_rect()):
- return True
- return False
-
- def draw(self, surf):
- # for hitbox in self.hitboxes:
- # pygame.draw.rect(surf, RED, hitbox)
- # for pos, time in self.sliced_points:
- # pygame.draw.circle(surf, RED, pos, 10)
- if len(self.sliced_points) > 1:
- pygame.draw.lines(surf, BLACK, False, [a for a, b in self.sliced_points], 3)
+++ /dev/null
-import pygame
-import random
-
-pygame.init()
-WIDTH, HEIGHT = pygame.display.Info().current_w, pygame.display.Info().current_h
-
-screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.FULLSCREEN)
-
-pygame.display.set_caption("Fruit Ninja")
-
-# colors
-WHITE = (255, 255, 255)
-LIGHT_GRAY = (211, 211, 211)
-GRAY = (128, 128, 128)
-DARK_GRAY = (25, 25, 25)
-BLACK = (0, 0, 0)
-
-RED = (255, 0, 0)
-GREEN = (0, 255, 0)
-BLUE = (0, 0, 255)
-ORANGE = (255, 165, 0)
-YELLOW = (255, 255, 0)
-PURPLE = (128, 0, 128)
-
-BROWN = (139, 69, 19)
-
-DARK_RED = (139, 0, 0)
-DARK_GREEN = (0, 100, 0)
-DARK_BLUE = (0, 0, 139)
-DARK_ORANGE = (255, 140, 0)
-DARK_YELLOW = (255, 215, 0)
-DARK_PURPLE = (75, 0, 130)
-DARKEN = {RED: DARK_RED, ORANGE: DARK_ORANGE, YELLOW: DARK_YELLOW, GREEN: DARK_GREEN, BLUE: DARK_BLUE,
- PURPLE: DARK_PURPLE, WHITE: GRAY}
-
-# commands
-COMMAND_EXIT = 0
-COMMAND_START = 1
-
-
-def lerp(start, end, weight):
- return weight * (end - start) + start
-
-
-def clamp(value, minimum, maximum):
- return min(maximum, max(minimum, value))
-
-
-font = pygame.font.SysFont("Arial", 50)
-font_large = pygame.font.SysFont("Arial", 80)