initial commit
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
build/
|
||||
env/
|
||||
__pycache__/
|
||||
*.db
|
||||
BIN
Logo/PythonGame (1).jpg
Normal file
|
After Width: | Height: | Size: 222 KiB |
BIN
Logo/PythonGame (1).png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
Logo/PythonGame-removebg.png
Normal file
|
After Width: | Height: | Size: 3 MiB |
BIN
Logo/PythonGame.jpg
Normal file
|
After Width: | Height: | Size: 449 KiB |
BIN
Logo/logo.ico
Normal file
|
After Width: | Height: | Size: 66 KiB |
61
README.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<div class="title-logo">
|
||||
<h1>Serpent's Legacy <em class="otherName">(Path of the Loner)</em></h1>
|
||||
<img src="https://github.com/ExostFlashPro/path-of-the-loner/blob/main/Logo/PythonGame%20(1).png?raw=true" alt="Serpent's Legacy Logo">
|
||||
</div>
|
||||
|
||||
## Project Overview
|
||||
|
||||
**Serpent's Legacy** is a game developed in **Python** using **object-oriented programming**. It offers an immersive experience with **PVP** and **PVE** combat, where players embody different heroes and face increasingly difficult enemies.
|
||||
|
||||
## Features
|
||||
|
||||
- **Three character classes**: Archer, Warrior, Mage
|
||||
- **Equipment system**: Customizable armor and weapons
|
||||
- **PVP mode**: Face other players in strategic duels
|
||||
- **PVE mode**: Progress through the **Hall of the Fallen**, consisting of multiple rooms with increasing monster difficulty
|
||||
|
||||
## Technologies Used
|
||||
|
||||
- **Language**: Python
|
||||
- **Paradigm**: Object-oriented programming
|
||||
- **Frameworks & Libraries**: sty,
|
||||
|
||||
## Project Management
|
||||
|
||||
The project was carried out with a structured approach, divided into several **milestones** to ensure smooth and efficient progress. Task tracking and scheduling helped optimize development and ensure that objectives were met.
|
||||
|
||||
## Future Improvements
|
||||
|
||||
Several areas for improvement have been identified to enhance the game, including:
|
||||
|
||||
- **Graphics**: Improving visuals and UI for better gameplay
|
||||
- **Artificial Intelligence**: Making enemies more dynamic and strategic
|
||||
- **Database**: Optimizing data storage and player management
|
||||
|
||||
## How to Run the Project
|
||||
|
||||
### <span class="method">Method 1:</span> Clone the repository and run the executable
|
||||
|
||||
1. **Clone the repository**:
|
||||
```bash
|
||||
git clone <REPO_URL>
|
||||
cd path-of-the-Loner
|
||||
```
|
||||
2. **Run the game**:
|
||||
- Navigate to the dist folder
|
||||
- Launch the provided executable
|
||||
|
||||
### <span class="method">Method 2:</span> Download the executable directly
|
||||
|
||||
1. Download the available executable from the dist folder
|
||||
2. Run the game directly without any additional installation
|
||||
|
||||
|
||||
## Authors
|
||||
|
||||
- **ClemcleRagazzo** _(MOULIS Clément)_
|
||||
- **ExostFlash** _(MAIZY Amaury)_
|
||||
|
||||
## Supervisor
|
||||
|
||||
- **Sylent** _(HASSAN Aunim)_
|
||||
30006
Sujet/POO - Path Of The Loner.pdf
Normal file
BIN
Sujet/Présentation.pptx
Normal file
85
Sujet/README - Real.md
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<style>
|
||||
.title-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 100px; /* Espacement entre le logo et le texte */
|
||||
}
|
||||
.title-logo img {
|
||||
width: 150px;
|
||||
height: auto;
|
||||
}
|
||||
h1 {
|
||||
color: rgb(245, 167, 0);
|
||||
}
|
||||
.otherName {
|
||||
color: rgba(245, 167, 0, 0.4)
|
||||
}
|
||||
h2 {
|
||||
color: rgb(23, 137, 252);
|
||||
}
|
||||
.method {
|
||||
color: rgb(120, 194, 253);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="title-logo">
|
||||
<h1>Serpent's Legacy <em class="otherName">(Path of the Loner)</em></h1>
|
||||
<img src="./Logo/PythonGame (1).png" alt="Serpent's Legacy Logo">
|
||||
</div>
|
||||
|
||||
## Project Overview
|
||||
|
||||
**Serpent's Legacy** is a game developed in **Python** using **object-oriented programming**. It offers an immersive experience with **PVP** and **PVE** combat, where players embody different heroes and face increasingly difficult enemies.
|
||||
|
||||
## Features
|
||||
|
||||
- **Three character classes**: Archer, Warrior, Mage
|
||||
- **Equipment system**: Customizable armor and weapons
|
||||
- **PVP mode**: Face other players in strategic duels
|
||||
- **PVE mode**: Progress through the **Hall of the Fallen**, consisting of multiple rooms with increasing monster difficulty
|
||||
|
||||
## Technologies Used
|
||||
|
||||
- **Language**: Python
|
||||
- **Paradigm**: Object-oriented programming
|
||||
- **Frameworks & Libraries**: (To be completed if necessary, e.g., Pygame, Tkinter, etc.)
|
||||
|
||||
## Project Management
|
||||
|
||||
The project was carried out with a structured approach, divided into several **milestones** to ensure smooth and efficient progress. Task tracking and scheduling helped optimize development and ensure that objectives were met.
|
||||
|
||||
## Future Improvements
|
||||
|
||||
Several areas for improvement have been identified to enhance the game, including:
|
||||
|
||||
- **Graphics**: Improving visuals and UI for better gameplay
|
||||
- **Artificial Intelligence**: Making enemies more dynamic and strategic
|
||||
- **Database**: Optimizing data storage and player management
|
||||
|
||||
## How to Run the Project
|
||||
|
||||
### <span class="method">Method 1:</span> Clone the repository and run the executable
|
||||
|
||||
1. **Clone the repository**:
|
||||
```bash
|
||||
git clone <REPO_URL>
|
||||
cd path-of-the-Loner
|
||||
```
|
||||
2. **Run the game**:
|
||||
- Navigate to the dist folder
|
||||
- Launch the provided executable
|
||||
|
||||
### <span class="method">Method 2:</span> Download the executable directly
|
||||
|
||||
1. Download the available executable from the dist folder
|
||||
2. Run the game directly without any additional installation
|
||||
|
||||
|
||||
## Authors
|
||||
|
||||
- **ClemcleRagazzo** _(MOULIS Clément)_
|
||||
- **ExostFlash** _(MAIZY Amaury)_
|
||||
|
||||
## Supervisor
|
||||
|
||||
- **Sylent** _(HASSAN Aunim)_
|
||||
47
controllers/PveController.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# pveController
|
||||
|
||||
from views.PVP.chooseCharacterUI import ChooseCharacterUI
|
||||
from views.PVE.roomUI import RoomUI
|
||||
|
||||
from sty import fg
|
||||
|
||||
# pour éviter les pb d'import circulaire
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from controllers.mainController import MainController
|
||||
|
||||
|
||||
class PveController:
|
||||
|
||||
def __init__(self, mController: "MainController") -> None:
|
||||
self.mController = mController
|
||||
self.db = self.mController.db
|
||||
MAX_HEROES = 3
|
||||
|
||||
self.heroes = []
|
||||
choice = -1
|
||||
|
||||
while choice < 1 or choice > MAX_HEROES:
|
||||
print(f"How many heroes do you want in your team ?")
|
||||
print(f"Choose between {fg.blue}1{fg.rs} and {fg.blue}{MAX_HEROES}{fg.rs}")
|
||||
|
||||
try:
|
||||
choice = int(input("How many heroes do you want: "))
|
||||
except ValueError:
|
||||
print(f"{fg.red} Input a valid number {fg.rs}")
|
||||
continue
|
||||
|
||||
if choice < 1 or choice > MAX_HEROES:
|
||||
print(f"{fg.red}Invalid choice. Try again {fg.rs}")
|
||||
|
||||
for i in range(0, choice):
|
||||
self.heroes.append(ChooseCharacterUI(self, False).hero)
|
||||
|
||||
RoomUI(self, 50, self.heroes)
|
||||
RoomUI(self, 100, self.heroes)
|
||||
RoomUI(self, 150, self.heroes)
|
||||
RoomUI(self, 200, self.heroes)
|
||||
|
||||
if self.heroes:
|
||||
print("You passed all rooms")
|
||||
37
controllers/PvpController.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# pvpController
|
||||
|
||||
# views
|
||||
from views.PVP.chooseCharacterUI import ChooseCharacterUI
|
||||
from views.PVP.arenaUI import ArenaUI
|
||||
|
||||
# models
|
||||
from models.heroes.hero import Hero
|
||||
from models.heroes.archery import Archery
|
||||
from models.heroes.warrior import Warrior
|
||||
from models.heroes.wizard import Wizard
|
||||
|
||||
# pour éviter les pb d'import circulaire
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from controllers.mainController import MainController
|
||||
|
||||
|
||||
class PvpController:
|
||||
def __init__(self, mController: "MainController") -> None:
|
||||
self.mController = mController
|
||||
self.db = self.mController.db
|
||||
|
||||
self.hero: Hero | Archery | Warrior | Wizard = ChooseCharacterUI(
|
||||
self, False
|
||||
).hero
|
||||
|
||||
print("---------------")
|
||||
print("Création du personnage")
|
||||
self.opponent: Hero | Archery | Warrior | Wizard = ChooseCharacterUI(
|
||||
self, True
|
||||
).hero
|
||||
|
||||
print("---------------")
|
||||
|
||||
ArenaUI(self)
|
||||
43
controllers/mainController.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# mainController
|
||||
|
||||
from controllers.PvpController import PvpController
|
||||
from controllers.PveController import PveController
|
||||
|
||||
from database.mainDB import MainDB
|
||||
|
||||
from sty import fg
|
||||
|
||||
|
||||
class MainController:
|
||||
def __init__(self) -> None:
|
||||
self.db = MainDB()
|
||||
|
||||
self.init()
|
||||
|
||||
def init(self):
|
||||
PVP_MODE = 1
|
||||
PVE_MODE = 2
|
||||
game_mode = [PVP_MODE, PVE_MODE]
|
||||
|
||||
choice = 0
|
||||
while choice not in game_mode:
|
||||
print("Choose your game mode")
|
||||
print(f"{PVP_MODE}. PVP mode")
|
||||
print(f"{PVE_MODE}. PVE mode")
|
||||
|
||||
try:
|
||||
choice = int(input("Choose your gamemode: "))
|
||||
except ValueError:
|
||||
print(f"{fg.red}Input a valid number{fg.rs}")
|
||||
continue
|
||||
|
||||
if choice == PVP_MODE:
|
||||
PvpController(self)
|
||||
elif choice == PVE_MODE:
|
||||
PveController(self)
|
||||
else:
|
||||
raise Exception("An error occured")
|
||||
|
||||
def close_game(self):
|
||||
input("Press any key to quit game")
|
||||
self.db.close_db()
|
||||
BIN
database/SQLiteSpy.exe
Normal file
51
database/databases/armorDB.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# armorDB
|
||||
|
||||
from models.loots.armor import Armor
|
||||
from models.heroes.hero import Hero
|
||||
from models.loots.loot import Loot
|
||||
|
||||
from database.databases.lootDB import LootDB
|
||||
|
||||
|
||||
# pour éviter les pb d'import circulaire
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from database.mainDB import MainDB
|
||||
|
||||
|
||||
class ArmorDB(LootDB):
|
||||
|
||||
def __init__(self, db: "MainDB") -> None:
|
||||
self.db = db
|
||||
|
||||
def get_armor(self) -> list[Armor]:
|
||||
query = "SELECT * FROM loots WHERE types_id = ?"
|
||||
values = (5,)
|
||||
|
||||
self.db.sql_query(query, values)
|
||||
|
||||
results = self.db.cursor.fetchall()
|
||||
|
||||
list_return = []
|
||||
for result in results:
|
||||
list_return.append(self.define_loot(result))
|
||||
|
||||
return list_return
|
||||
|
||||
def define_loot(self, result: list) -> Armor:
|
||||
id = result[0]
|
||||
title = result[1]
|
||||
type = result[2]
|
||||
loot_value = result[3]
|
||||
value = result[4]
|
||||
description = result[5]
|
||||
drop_rate = result[6]
|
||||
|
||||
armor = Armor(title, description, type, loot_value, value, drop_rate)
|
||||
armor.id = id
|
||||
|
||||
return armor
|
||||
|
||||
def get_armor_by_hero_type(self, hero: Hero) -> list[Armor]:
|
||||
return self.get_loots_by_hero_and_loot_type(hero, Loot.ARMOR)
|
||||
114
database/databases/heroDB.py
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
# heroDB
|
||||
|
||||
from models.heroes.hero import Hero
|
||||
from models.heroes.archery import Archery
|
||||
from models.heroes.warrior import Warrior
|
||||
from models.heroes.wizard import Wizard
|
||||
|
||||
|
||||
# pour éviter les pb d'import circulaire
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from database.mainDB import MainDB
|
||||
|
||||
|
||||
class HeroDB:
|
||||
def __init__(self, db: "MainDB") -> None:
|
||||
self.db = db
|
||||
|
||||
def get_all_heroes(self) -> list[Hero]:
|
||||
query = "SELECT * FROM heroes WHERE ?"
|
||||
values = (1,)
|
||||
|
||||
self.db.sql_query(query, values)
|
||||
|
||||
results = self.db.cursor.fetchall()
|
||||
|
||||
heroes = []
|
||||
for result in results:
|
||||
heroes.append(self.define_hero(result))
|
||||
|
||||
return heroes
|
||||
|
||||
def get_archers(self) -> list[Hero]:
|
||||
query = "SELECT * FROM heroes WHERE types_id = ?"
|
||||
values = (Hero.ARCHERY,)
|
||||
|
||||
self.db.sql_query(query, values)
|
||||
|
||||
results = self.db.cursor.fetchall()
|
||||
|
||||
list_return = []
|
||||
for result in results:
|
||||
list_return.append(self.define_hero(result))
|
||||
|
||||
return list_return
|
||||
|
||||
def get_warriors(self) -> list[Hero]:
|
||||
query = "SELECT * FROM heroes WHERE types_id = ?"
|
||||
values = (Hero.WARRIOR,)
|
||||
|
||||
self.db.sql_query(query, values)
|
||||
|
||||
results = self.db.cursor.fetchall()
|
||||
|
||||
list_return = []
|
||||
for result in results:
|
||||
list_return.append(self.define_hero(result))
|
||||
|
||||
return list_return
|
||||
|
||||
def get_wizards(self) -> list[Hero]:
|
||||
query = "SELECT * FROM heroes WHERE types_id = ?"
|
||||
values = (Hero.WIZARD,)
|
||||
|
||||
self.db.sql_query(query, values)
|
||||
|
||||
results = self.db.cursor.fetchall()
|
||||
|
||||
list_return = []
|
||||
for result in results:
|
||||
list_return.append(self.define_hero(result))
|
||||
|
||||
return list_return
|
||||
|
||||
def define_hero(self, result: list) -> Hero:
|
||||
id = result[0]
|
||||
title = result[1]
|
||||
types_id = result[2]
|
||||
health_points = result[3]
|
||||
attack_power = result[4]
|
||||
defense = result[5]
|
||||
mana = result[6]
|
||||
description = result[7]
|
||||
|
||||
if types_id == Hero.ARCHERY:
|
||||
hero = Archery(
|
||||
title,
|
||||
description,
|
||||
health_points,
|
||||
attack_power,
|
||||
defense,
|
||||
)
|
||||
elif types_id == Hero.WARRIOR:
|
||||
hero = Warrior(
|
||||
title,
|
||||
description,
|
||||
health_points,
|
||||
attack_power,
|
||||
defense,
|
||||
)
|
||||
elif types_id == Hero.WIZARD:
|
||||
hero = Wizard(
|
||||
title,
|
||||
description,
|
||||
health_points,
|
||||
attack_power,
|
||||
defense,
|
||||
mana,
|
||||
)
|
||||
|
||||
hero.id = id
|
||||
|
||||
return hero
|
||||
50
database/databases/lootDB.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# lootDB
|
||||
|
||||
from models.loots.loot import Loot
|
||||
from models.heroes.monster import Monster
|
||||
from models.heroes.hero import Hero
|
||||
|
||||
# pour éviter les pb d'import circulaire
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from database.mainDB import MainDB
|
||||
|
||||
|
||||
class LootDB:
|
||||
|
||||
def __init__(self, db: "MainDB") -> None:
|
||||
self.db = db
|
||||
|
||||
def get_loots_by_hero_and_loot_type(self, hero: Hero, loot_type: int) -> list:
|
||||
query = """
|
||||
SELECT l.*
|
||||
FROM loots l
|
||||
INNER JOIN characters_has_loots chl ON l.id = chl.loot_type
|
||||
WHERE chl.character_type = ? AND l.types_id = ?;
|
||||
"""
|
||||
values = (hero.type, loot_type)
|
||||
|
||||
self.db.sql_query(query, values)
|
||||
results = self.db.cursor.fetchall()
|
||||
|
||||
loots = []
|
||||
for result in results:
|
||||
loots.append(self.define_loot(result))
|
||||
|
||||
return loots
|
||||
|
||||
def define_loot(self, result: list) -> dict:
|
||||
id = result[0]
|
||||
title = result[1]
|
||||
type = result[2]
|
||||
loot_value = result[3]
|
||||
value = result[4]
|
||||
description = result[5]
|
||||
drop_rate = result[6]
|
||||
|
||||
loot = Loot(title, description, type, loot_value, value, drop_rate)
|
||||
loot.id = id
|
||||
|
||||
return loot
|
||||
51
database/databases/monsterDB.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# monsterDB
|
||||
|
||||
from models.heroes.monster import Monster
|
||||
|
||||
# pour éviter les pb d'import circulaire
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from database.mainDB import MainDB
|
||||
|
||||
|
||||
class MonsterDB:
|
||||
def __init__(self, db: "MainDB") -> None:
|
||||
self.db = db
|
||||
|
||||
def get_all_monsters(self) -> list[Monster]:
|
||||
query = "SELECT * FROM monsters WHERE types_id IN (?, ?, ?, ?, ?)"
|
||||
values = (
|
||||
Monster.BEAST,
|
||||
Monster.HUMANOID,
|
||||
Monster.UNDEAD,
|
||||
Monster.DRAGON,
|
||||
Monster.ELEMENTAL,
|
||||
)
|
||||
|
||||
self.db.sql_query(query, values)
|
||||
|
||||
results = self.db.cursor.fetchall()
|
||||
|
||||
monsters = []
|
||||
for result in results:
|
||||
monsters.append(self.define_monster(result))
|
||||
|
||||
return monsters
|
||||
|
||||
def define_monster(self, result: list) -> Monster:
|
||||
id = result[0]
|
||||
title = result[1]
|
||||
types_id = result[2]
|
||||
health_points = result[3]
|
||||
attack_power = result[4]
|
||||
defense = result[5]
|
||||
description = result[6]
|
||||
|
||||
monster = Monster(
|
||||
title, description, health_points, attack_power, defense, types_id
|
||||
)
|
||||
|
||||
monster.id = id
|
||||
|
||||
return monster
|
||||
51
database/databases/weaponDB.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# weaponDB
|
||||
|
||||
from models.loots.weapon import Weapon
|
||||
from models.heroes.hero import Hero
|
||||
from models.loots.loot import Loot
|
||||
|
||||
from database.databases.lootDB import LootDB
|
||||
|
||||
|
||||
# pour éviter les pb d'import circulaire
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from database.mainDB import MainDB
|
||||
|
||||
|
||||
class WeaponDB(LootDB):
|
||||
|
||||
def __init__(self, db: "MainDB") -> None:
|
||||
self.db = db
|
||||
|
||||
def get_weapons(self) -> list[Weapon]:
|
||||
query = "SELECT * FROM loots WHERE types_id = ?"
|
||||
values = (Weapon.WEAPON,)
|
||||
|
||||
self.db.sql_query(query, values)
|
||||
|
||||
results = self.db.cursor.fetchall()
|
||||
|
||||
list_return = []
|
||||
for result in results:
|
||||
list_return.append(self.define_loot(result))
|
||||
|
||||
return list_return
|
||||
|
||||
def define_loot(self, result: list) -> Weapon:
|
||||
id = result[0]
|
||||
title = result[1]
|
||||
type = result[2]
|
||||
loot_value = result[3]
|
||||
value = result[4]
|
||||
description = result[5]
|
||||
drop_rate = result[6]
|
||||
|
||||
weapon = Weapon(title, description, type, loot_value, value, drop_rate)
|
||||
weapon.id = id
|
||||
|
||||
return weapon
|
||||
|
||||
def get_weapon_by_hero_type(self, hero: Hero) -> list[Weapon]:
|
||||
return self.get_loots_by_hero_and_loot_type(hero, Loot.WEAPON)
|
||||
561
database/mainDB.py
Normal file
|
|
@ -0,0 +1,561 @@
|
|||
import sqlite3
|
||||
|
||||
from database.databases.armorDB import ArmorDB
|
||||
from database.databases.weaponDB import WeaponDB
|
||||
from database.databases.heroDB import HeroDB
|
||||
from database.databases.monsterDB import MonsterDB
|
||||
|
||||
from models.heroes.hero import Hero
|
||||
|
||||
import os
|
||||
|
||||
FOLDER_DB = "data"
|
||||
FILE_DB = "mydb.db"
|
||||
FILE_PATH_DB = os.path.join(os.getcwd(), FOLDER_DB, FILE_DB)
|
||||
|
||||
|
||||
class MainDB:
|
||||
def __init__(self) -> None:
|
||||
self.were_already_existing = True
|
||||
|
||||
if not os.path.exists(FOLDER_DB):
|
||||
os.makedirs(FOLDER_DB)
|
||||
|
||||
if not os.path.exists(FILE_PATH_DB):
|
||||
with open(FILE_PATH_DB, "w") as db_file:
|
||||
pass
|
||||
|
||||
self.were_already_existing = False
|
||||
|
||||
# Connexion à la base de données SQLite
|
||||
self.conn = sqlite3.connect(FILE_PATH_DB)
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
self.create_tables()
|
||||
|
||||
self.add_databases()
|
||||
|
||||
# print(
|
||||
# f"Database '{FILE_PATH_DB}' and tables with initial data created successfully."
|
||||
# )
|
||||
|
||||
def close_db(self):
|
||||
self.conn.close()
|
||||
|
||||
def add_databases(self):
|
||||
self.weapon_db = WeaponDB(self)
|
||||
self.armor_db = ArmorDB(self)
|
||||
self.hero_db = HeroDB(self)
|
||||
self.monster_db = MonsterDB(self)
|
||||
|
||||
def create_tables(self):
|
||||
# Création des tables
|
||||
self.cursor.executescript(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS types (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS monsters (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT,
|
||||
types_id INTEGER NOT NULL,
|
||||
health_points INTEGER,
|
||||
attack_power REAL,
|
||||
defense REAL,
|
||||
description TEXT,
|
||||
FOREIGN KEY (types_id) REFERENCES types (id) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS values_of_loots (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT,
|
||||
description TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS loots (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT,
|
||||
types_id INTEGER NOT NULL,
|
||||
values_of_loots_id INTEGER NOT NULL,
|
||||
value INTEGER NOT NULL,
|
||||
description TEXT,
|
||||
drop_rate REAL,
|
||||
FOREIGN KEY (types_id) REFERENCES types (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
|
||||
FOREIGN KEY (values_of_loots_id) REFERENCES values_of_loots (id) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS heroes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT,
|
||||
types_id INTEGER NOT NULL,
|
||||
health_points INTEGER,
|
||||
attack_power REAL,
|
||||
defense REAL,
|
||||
mana INTEGER,
|
||||
description TEXT,
|
||||
FOREIGN KEY (types_id) REFERENCES types (id) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS characters_has_loots (
|
||||
character_type INTEGER NOT NULL,
|
||||
loot_type INTEGER NOT NULL,
|
||||
PRIMARY KEY (character_type, loot_type),
|
||||
FOREIGN KEY (character_type) REFERENCES heroes (id) ON DELETE NO ACTION ON UPDATE NO ACTION,
|
||||
FOREIGN KEY (loot_type) REFERENCES loots (id) ON DELETE NO ACTION ON UPDATE NO ACTION
|
||||
);
|
||||
"""
|
||||
)
|
||||
|
||||
if not self.were_already_existing:
|
||||
self.add_tables()
|
||||
|
||||
# print(f"Database and tables with expanded data created successfully.")
|
||||
|
||||
self.conn.commit()
|
||||
|
||||
def add_tables(self):
|
||||
|
||||
# Insertion des données dans la table types
|
||||
self.cursor.executemany(
|
||||
"""
|
||||
INSERT INTO types (title) VALUES (?);
|
||||
""",
|
||||
[
|
||||
("archer",),
|
||||
("warrior",),
|
||||
("mage",),
|
||||
("weapons",),
|
||||
("armor",),
|
||||
("consumable",),
|
||||
("beast",),
|
||||
("humanoid",),
|
||||
("undead",),
|
||||
("dragon",),
|
||||
("elemental",),
|
||||
],
|
||||
)
|
||||
|
||||
# Insertion des données dans la table values_of_loots
|
||||
self.cursor.executemany(
|
||||
"""
|
||||
INSERT INTO values_of_loots (title, description) VALUES (?, ?);
|
||||
""",
|
||||
[
|
||||
("attack", "The ability to deal damage to an opponent."),
|
||||
("defence", "The ability to reduce or block incoming damage."),
|
||||
("Healing", "The ability to restore lost health."),
|
||||
("Mana Boost", "Restores or increases mana."),
|
||||
("Critical Hit", "Increases chance of dealing critical damage."),
|
||||
],
|
||||
)
|
||||
|
||||
# Insertion des données dans la table loots
|
||||
self.cursor.executemany(
|
||||
"""
|
||||
INSERT INTO loots (title, types_id, values_of_loots_id, value, description, drop_rate)
|
||||
VALUES (?, ?, ?, ?, ?, ?);
|
||||
""",
|
||||
[
|
||||
(
|
||||
"Rusty Sword",
|
||||
4,
|
||||
1,
|
||||
14,
|
||||
"A worn-out sword, barely sharp enough to be effective.",
|
||||
40,
|
||||
),
|
||||
(
|
||||
"Enchanted Bow",
|
||||
4,
|
||||
1,
|
||||
5,
|
||||
"A magical bow with increased accuracy and damage.",
|
||||
25,
|
||||
),
|
||||
(
|
||||
"Mystic Robe",
|
||||
5,
|
||||
2,
|
||||
8,
|
||||
"A robe that enhances the wearer's defense against magic.",
|
||||
30,
|
||||
),
|
||||
(
|
||||
"Health Potion",
|
||||
6,
|
||||
3,
|
||||
0,
|
||||
"A potion that restores 50 health points.",
|
||||
60,
|
||||
), # Ajout de `0` pour value
|
||||
(
|
||||
"Iron Shield",
|
||||
5,
|
||||
2,
|
||||
7,
|
||||
"A sturdy shield made of iron, providing excellent defense.",
|
||||
40,
|
||||
),
|
||||
(
|
||||
"Fireball Scroll",
|
||||
6,
|
||||
1,
|
||||
14,
|
||||
"A scroll that teaches the fireball spell to a mage.",
|
||||
20,
|
||||
),
|
||||
(
|
||||
"Dragon Scale Armor",
|
||||
5,
|
||||
2,
|
||||
20,
|
||||
"Armor crafted from dragon scales, highly durable.",
|
||||
15,
|
||||
),
|
||||
(
|
||||
"Thunder Sword",
|
||||
4,
|
||||
1,
|
||||
8,
|
||||
"A sword infused with thunder magic.",
|
||||
20,
|
||||
),
|
||||
(
|
||||
"Arcane Scepter",
|
||||
4,
|
||||
1,
|
||||
9,
|
||||
"A powerful scepter that amplifies magic.",
|
||||
15,
|
||||
),
|
||||
(
|
||||
"Potion of damaging Power",
|
||||
4,
|
||||
1,
|
||||
3,
|
||||
"Temporarily increases spell power.",
|
||||
30,
|
||||
),
|
||||
(
|
||||
"Potion of Devastation",
|
||||
4,
|
||||
1,
|
||||
7,
|
||||
"Boosts magical damage for a short time.",
|
||||
25,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
# Insertion des données dans la table heroes
|
||||
self.cursor.executemany(
|
||||
"""
|
||||
INSERT INTO heroes (title, types_id, health_points, attack_power, defense, mana, description)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?);
|
||||
""",
|
||||
[
|
||||
(
|
||||
"Eldon the Brave",
|
||||
2,
|
||||
150,
|
||||
25.0,
|
||||
20.0,
|
||||
10,
|
||||
"A seasoned warrior known for his resilience and strength.",
|
||||
),
|
||||
(
|
||||
"Lyra the Swift",
|
||||
1,
|
||||
100,
|
||||
30.0,
|
||||
15.0,
|
||||
50,
|
||||
"An archer with unmatched speed and precision.",
|
||||
),
|
||||
(
|
||||
"Morgana the Wise",
|
||||
3,
|
||||
80,
|
||||
20.0,
|
||||
10.0,
|
||||
100,
|
||||
"A mage with a deep knowledge of arcane spells.",
|
||||
),
|
||||
(
|
||||
"Tharok the Shieldbearer",
|
||||
2,
|
||||
200,
|
||||
15.0,
|
||||
35.0,
|
||||
5,
|
||||
"A warrior who specializes in defensive tactics.",
|
||||
),
|
||||
(
|
||||
"Selene the Healer",
|
||||
3,
|
||||
90,
|
||||
10.0,
|
||||
10.0,
|
||||
120,
|
||||
"A mage dedicated to supporting her allies with healing magic.",
|
||||
),
|
||||
(
|
||||
"Ragnar the Fierce",
|
||||
2,
|
||||
170,
|
||||
35.0,
|
||||
25.0,
|
||||
15,
|
||||
"A barbarian warrior with immense strength and ferocity.",
|
||||
),
|
||||
(
|
||||
"Elara the Mystic",
|
||||
3,
|
||||
85,
|
||||
25.0,
|
||||
15.0,
|
||||
110,
|
||||
"A mage who commands the elements with finesse.",
|
||||
),
|
||||
(
|
||||
"Galdor the Archer",
|
||||
1,
|
||||
120,
|
||||
40.0,
|
||||
20.0,
|
||||
30,
|
||||
"A master archer with keen eyesight and precision.",
|
||||
),
|
||||
(
|
||||
"Fenrir the Wild",
|
||||
2,
|
||||
160,
|
||||
28.0,
|
||||
18.0,
|
||||
20,
|
||||
"A warrior with a primal fighting style.",
|
||||
),
|
||||
(
|
||||
"Aurora the Lightbringer",
|
||||
3,
|
||||
95,
|
||||
18.0,
|
||||
12.0,
|
||||
130,
|
||||
"A mage who wields the power of light.",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
# Insertion des données dans la table monsters
|
||||
self.cursor.executemany(
|
||||
"""
|
||||
INSERT INTO monsters (title, types_id, health_points, attack_power, defense, description)
|
||||
VALUES (?, ?, ?, ?, ?, ?);
|
||||
""",
|
||||
[
|
||||
(
|
||||
"Goblin Grunt",
|
||||
7,
|
||||
50,
|
||||
10.0,
|
||||
5.0,
|
||||
"A small but vicious creature, often found in caves.",
|
||||
),
|
||||
(
|
||||
"Forest Troll",
|
||||
7,
|
||||
200,
|
||||
30.0,
|
||||
25.0,
|
||||
"A massive troll that roams the dense forests.",
|
||||
),
|
||||
(
|
||||
"Dark Sorcerer",
|
||||
8,
|
||||
150,
|
||||
40.0,
|
||||
10.0,
|
||||
"A rogue magician who practices forbidden dark magic.",
|
||||
),
|
||||
(
|
||||
"Shadow Wolf",
|
||||
7,
|
||||
100,
|
||||
20.0,
|
||||
10.0,
|
||||
"A wolf with fur as dark as the night, moving silently.",
|
||||
),
|
||||
(
|
||||
"Skeleton Warrior",
|
||||
9,
|
||||
80,
|
||||
15.0,
|
||||
10.0,
|
||||
"An animated skeleton armed with rusted weapons.",
|
||||
),
|
||||
(
|
||||
"Fire Drake",
|
||||
10,
|
||||
250,
|
||||
50.0,
|
||||
30.0,
|
||||
"A small dragon with the ability to breathe fire.",
|
||||
),
|
||||
(
|
||||
"Ice Elemental",
|
||||
11,
|
||||
180,
|
||||
35.0,
|
||||
20.0,
|
||||
"A being composed of ice, freezing everything it touches.",
|
||||
),
|
||||
(
|
||||
"Bandit Leader",
|
||||
8,
|
||||
120,
|
||||
25.0,
|
||||
15.0,
|
||||
"The cunning leader of a group of bandits.",
|
||||
),
|
||||
(
|
||||
"Cave Spider",
|
||||
7,
|
||||
60,
|
||||
15.0,
|
||||
5.0,
|
||||
"A giant spider that dwells in dark caves.",
|
||||
),
|
||||
(
|
||||
"Wraith",
|
||||
9,
|
||||
110,
|
||||
20.0,
|
||||
10.0,
|
||||
"A ghostly figure that haunts abandoned places.",
|
||||
),
|
||||
(
|
||||
"Lava Golem",
|
||||
10,
|
||||
300,
|
||||
60.0,
|
||||
40.0,
|
||||
"A hulking creature made of molten rock.",
|
||||
),
|
||||
(
|
||||
"Thunderbird",
|
||||
10,
|
||||
220,
|
||||
45.0,
|
||||
25.0,
|
||||
"A mythical bird that controls storms.",
|
||||
),
|
||||
(
|
||||
"Necromancer",
|
||||
8,
|
||||
160,
|
||||
50.0,
|
||||
20.0,
|
||||
"A dark mage who raises the dead to do their bidding.",
|
||||
),
|
||||
(
|
||||
"Dire Bear",
|
||||
7,
|
||||
240,
|
||||
35.0,
|
||||
25.0,
|
||||
"A massive bear with unparalleled strength.",
|
||||
),
|
||||
(
|
||||
"Sea Serpent",
|
||||
10,
|
||||
280,
|
||||
55.0,
|
||||
30.0,
|
||||
"A serpentine monster that lurks in deep waters.",
|
||||
),
|
||||
(
|
||||
"Harpy",
|
||||
8,
|
||||
100,
|
||||
20.0,
|
||||
15.0,
|
||||
"A winged monster with sharp claws and a screeching cry.",
|
||||
),
|
||||
(
|
||||
"Stone Guardian",
|
||||
11,
|
||||
320,
|
||||
40.0,
|
||||
50.0,
|
||||
"A statue brought to life to guard ancient ruins.",
|
||||
),
|
||||
(
|
||||
"Venomous Scorpion",
|
||||
7,
|
||||
90,
|
||||
25.0,
|
||||
10.0,
|
||||
"A giant scorpion with a deadly stinger.",
|
||||
),
|
||||
(
|
||||
"Zombie Horde",
|
||||
9,
|
||||
150,
|
||||
10.0,
|
||||
5.0,
|
||||
"A group of mindless zombies seeking flesh.",
|
||||
),
|
||||
(
|
||||
"Phoenix",
|
||||
10,
|
||||
200,
|
||||
40.0,
|
||||
20.0,
|
||||
"A mythical bird that rises from its ashes.",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
# Insertion des données dans la table monsters_has_loots
|
||||
self.cursor.executemany(
|
||||
"""
|
||||
INSERT INTO characters_has_loots (character_type, loot_type)
|
||||
VALUES (?, ?);
|
||||
""",
|
||||
[
|
||||
(Hero.WARRIOR, 1),
|
||||
(Hero.WARRIOR, 5),
|
||||
(Hero.WARRIOR, 8),
|
||||
(Hero.ARCHERY, 2),
|
||||
(Hero.ARCHERY, 5),
|
||||
(Hero.WIZARD, 9),
|
||||
(Hero.WIZARD, 10),
|
||||
(Hero.WIZARD, 11),
|
||||
(Hero.WIZARD, 3),
|
||||
],
|
||||
)
|
||||
|
||||
def sql_query(self, query: str, values: tuple, is_selecting=False):
|
||||
try:
|
||||
self.cursor.execute(query, values)
|
||||
if not is_selecting:
|
||||
self.conn.commit()
|
||||
return True
|
||||
except sqlite3.IntegrityError as ie:
|
||||
print(f"Insert DB error {ie}")
|
||||
except sqlite3.OperationalError as oe:
|
||||
print(f"Operational error: {oe}")
|
||||
print("Parameters type:", [type(v) for v in values])
|
||||
print("Parameters value:", values)
|
||||
except sqlite3.DatabaseError as e:
|
||||
print("DB error:", e)
|
||||
except sqlite3.InterfaceError as ie:
|
||||
print(f"Type Error: {ie}")
|
||||
print("Parameters type:", [type(v) for v in values])
|
||||
print("Parameters value:", values)
|
||||
except Exception as e:
|
||||
print("An SQL error occured:", e)
|
||||
return False
|
||||
BIN
database/mdl/V1.mwb
Normal file
BIN
database/mdl/V1.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
database/mdl/V2.mwb
Normal file
BIN
database/mdl/V2.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
database/mdl/V3.mwb
Normal file
BIN
database/mdl/V3.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
dist/Serpent s Legacy.exe
vendored
Normal file
19
main.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# main
|
||||
|
||||
from controllers.mainController import MainController
|
||||
|
||||
|
||||
def main():
|
||||
MainController()
|
||||
|
||||
|
||||
try:
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
except TypeError as te:
|
||||
print("Type Error:", te)
|
||||
except IOError as ie:
|
||||
print("IO Error", ie)
|
||||
except Exception as e:
|
||||
print("Error:", e)
|
||||
39
main.spec
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['main.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
name='Serpent s Legacy',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon='logo\logo.ico'
|
||||
)
|
||||
25
models/heroes/archery.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# archery
|
||||
|
||||
from models.loots.armor import Armor
|
||||
from models.loots.weapon import Weapon
|
||||
from models.heroes.hero import Hero
|
||||
|
||||
|
||||
class Archery(Hero):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
HP: int,
|
||||
attack_power: int,
|
||||
defense: int,
|
||||
armor: Armor = None,
|
||||
weapon: Weapon = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
name, description, HP, attack_power, defense, Hero.ARCHERY, armor, weapon
|
||||
)
|
||||
|
||||
def get_type_str(self) -> str:
|
||||
return "Archery"
|
||||
14
models/heroes/character.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# characterfg.da_magenta +
|
||||
|
||||
from sty import fg
|
||||
|
||||
|
||||
class Character:
|
||||
HERO = 1
|
||||
MONSTER = 2
|
||||
|
||||
def __init__(self, name: str, description: str, HP: int) -> None:
|
||||
self.id: int = None
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.HP = HP
|
||||
79
models/heroes/hero.py
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# hero
|
||||
|
||||
from models.heroes.character import Character
|
||||
from models.loots.armor import Armor
|
||||
from models.loots.weapon import Weapon
|
||||
|
||||
from sty import fg
|
||||
|
||||
|
||||
class Hero(Character):
|
||||
ARCHERY = 1
|
||||
WARRIOR = 2
|
||||
WIZARD = 3
|
||||
BEAST = 7
|
||||
HUMANOID = 8
|
||||
UNDEAD = 9
|
||||
DRAGON = 10
|
||||
ELEMENTAL = 11
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
HP: int,
|
||||
attack_power: int,
|
||||
defense: int,
|
||||
type: str,
|
||||
armor: Armor = None,
|
||||
weapon: Weapon = None,
|
||||
) -> None:
|
||||
super().__init__(name, description, HP)
|
||||
self.attack_power = attack_power
|
||||
self.defense = defense
|
||||
self.type = type
|
||||
self.armor = armor
|
||||
self.weapon = weapon
|
||||
|
||||
self.attacks_choice: list = ["Attacks", "Cure"]
|
||||
|
||||
def get_type_str(self) -> str:
|
||||
return "Heroes"
|
||||
|
||||
def attacks(self, opponent: "Hero") -> float:
|
||||
power = None
|
||||
if self.weapon:
|
||||
print(f"{self.name} {fg.red}attacks{fg.rs} with {self.weapon.name}")
|
||||
power = self.weapon.power
|
||||
else:
|
||||
print(
|
||||
f"{self.name} {fg.red}attacks{fg.rs} with {fg.yellow}bare hands{fg.rs}"
|
||||
)
|
||||
power = self.attack_power
|
||||
|
||||
if opponent.armor:
|
||||
power -= opponent.defends()
|
||||
|
||||
return power
|
||||
|
||||
def defends(self) -> float:
|
||||
if self.armor:
|
||||
print(
|
||||
f"{self.armor.name} {fg.blue}delete{fg.rs} {self.armor.defense} damage points"
|
||||
)
|
||||
return self.armor.defense
|
||||
else:
|
||||
return 0
|
||||
|
||||
def cure(self, hero: "Hero" = None):
|
||||
target = hero if hero else self
|
||||
|
||||
UP_HP = 10
|
||||
print(
|
||||
f"{target.name}: {target.HP} {fg.green}HP{fg.rs} +{UP_HP}{fg.green}HP{fg.rs}"
|
||||
)
|
||||
target.HP += UP_HP
|
||||
|
||||
print(f"{target.name}: {target.HP} {fg.green}HP{fg.rs}")
|
||||
|
||||
return UP_HP
|
||||
18
models/heroes/monster.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# monster
|
||||
|
||||
from models.heroes.hero import Hero
|
||||
|
||||
from sty import fg
|
||||
|
||||
|
||||
class Monster(Hero):
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
HP: int,
|
||||
attack_power: int,
|
||||
defense: int,
|
||||
type: str,
|
||||
) -> None:
|
||||
super().__init__(name, description, HP, attack_power, defense, type, None, None)
|
||||
32
models/heroes/warrior.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# warrior
|
||||
|
||||
from models.loots.armor import Armor
|
||||
from models.loots.weapon import Weapon
|
||||
from models.heroes.hero import Hero
|
||||
|
||||
|
||||
class Warrior(Hero):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
HP: int,
|
||||
attack_power: int,
|
||||
defense: int,
|
||||
armor: Armor = None,
|
||||
weapon: Weapon = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
name, description, HP, attack_power, defense, Hero.WARRIOR, armor, weapon
|
||||
)
|
||||
|
||||
def get_type_str(self) -> str:
|
||||
return "Warrior"
|
||||
|
||||
def attacks(self, opponent: "Hero") -> float:
|
||||
damage = 0
|
||||
for i in range(0, 2):
|
||||
damage += super().attacks(opponent)
|
||||
|
||||
return damage
|
||||
27
models/heroes/wizard.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# wizard
|
||||
|
||||
from models.loots.armor import Armor
|
||||
from models.loots.weapon import Weapon
|
||||
from models.heroes.hero import Hero
|
||||
|
||||
|
||||
class Wizard(Hero):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
HP: int,
|
||||
attack_power: int,
|
||||
defense: int,
|
||||
mana_stock: int,
|
||||
armor: Armor = None,
|
||||
weapon: Weapon = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
name, description, HP, attack_power, defense, Hero.WIZARD, armor, weapon
|
||||
)
|
||||
self.mana_stock = mana_stock
|
||||
|
||||
def get_type_str(self) -> str:
|
||||
return "Wizard"
|
||||
20
models/loots/armor.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# armor
|
||||
|
||||
from models.loots.loot import Loot
|
||||
|
||||
|
||||
class Armor(Loot):
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
type: int,
|
||||
loot_value: int,
|
||||
defense: int,
|
||||
drop_rate: int,
|
||||
) -> None:
|
||||
super().__init__(name, description, type, loot_value, defense, drop_rate)
|
||||
self.id = None
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.defense = defense
|
||||
24
models/loots/loot.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# loot
|
||||
|
||||
|
||||
class Loot:
|
||||
WEAPON = 4
|
||||
ARMOR = 5
|
||||
CONSUMABLE = 6
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
title: str,
|
||||
description: str,
|
||||
type: int,
|
||||
loot_value: int,
|
||||
value: int,
|
||||
drop_rate: int,
|
||||
) -> None:
|
||||
self.id: int = None
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.type = type
|
||||
self.loot_value = loot_value
|
||||
self.value = value
|
||||
self.drop_rate = drop_rate
|
||||
20
models/loots/weapon.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# weapon
|
||||
|
||||
from models.loots.loot import Loot
|
||||
|
||||
|
||||
class Weapon(Loot):
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
type: int,
|
||||
loot_value: int,
|
||||
power: int,
|
||||
drop_rate: int,
|
||||
) -> None:
|
||||
super().__init__(name, description, type, loot_value, power, drop_rate)
|
||||
self.id = None
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.power = power
|
||||
BIN
requirements.txt
Normal file
61
views/PVE/chooseMonstersUI.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# chooseMonstersUI
|
||||
|
||||
from models.heroes.monster import Monster
|
||||
|
||||
from sty import fg
|
||||
from random import randint
|
||||
|
||||
# pour éviter les pb d'import circulaire
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from controllers.PveController import PveController
|
||||
|
||||
|
||||
class ChooseMonstersUI:
|
||||
def __init__(
|
||||
self, pveController: "PveController", difficulty: int, is_AI: bool
|
||||
) -> None:
|
||||
MAX_NUMBER_MONSTER = 3
|
||||
self.pveController = pveController
|
||||
self.is_AI = is_AI
|
||||
self.difficulty = difficulty
|
||||
|
||||
self.monster: Monster = None
|
||||
self.monsters: list[Monster] = []
|
||||
|
||||
for i in range(0, MAX_NUMBER_MONSTER):
|
||||
self.set_monster()
|
||||
|
||||
self.monsters.append(self.monster)
|
||||
|
||||
def set_monster(self):
|
||||
monsters = self.pveController.db.monster_db.get_all_monsters()
|
||||
len_monsters = len(monsters)
|
||||
|
||||
choice = -1 # Initialiser à une valeur qui n'est pas un index valide
|
||||
while choice < 1 or choice > len_monsters:
|
||||
for i in range(0, len_monsters):
|
||||
monster = monsters[i]
|
||||
if not self.is_AI:
|
||||
print(f"{i+1}. {monster.get_type_str()} - {monster.name}")
|
||||
|
||||
try:
|
||||
if self.is_AI:
|
||||
choice = randint(1, len_monsters)
|
||||
# else:
|
||||
# choice = int(input("Give your choice: "))
|
||||
except ValueError:
|
||||
print(f"{fg.red}Input a valid number{fg.rs}")
|
||||
continue
|
||||
|
||||
if choice < 1 or choice > len_monsters:
|
||||
print(f"{fg.red}Invalid choice. Try again{fg.rs}")
|
||||
|
||||
# L'indice dans la liste est `choice - 1`
|
||||
# car choice commence à partir de 1
|
||||
self.monster = monsters[choice - 1]
|
||||
self.monster.name = fg.da_blue + self.monster.name + fg.rs
|
||||
|
||||
if self.monster.HP > self.difficulty:
|
||||
choice = -1
|
||||
158
views/PVE/roomUI.py
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
# roomUI
|
||||
|
||||
from models.heroes.hero import Hero
|
||||
from models.heroes.monster import Monster
|
||||
from views.PVE.chooseMonstersUI import ChooseMonstersUI
|
||||
|
||||
from sty import fg
|
||||
from random import randint
|
||||
|
||||
# pour éviter les pb d'import circulaire
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from controllers.PveController import PveController
|
||||
|
||||
|
||||
class RoomUI:
|
||||
def __init__(
|
||||
self,
|
||||
pveController: "PveController",
|
||||
difficulty: int,
|
||||
heroes: list[Hero],
|
||||
) -> None:
|
||||
self.pveController = pveController
|
||||
self.difficulty = difficulty
|
||||
self.heroes = heroes
|
||||
self.monsters: list[Monster] = None
|
||||
|
||||
if self.heroes:
|
||||
print("Monsters creation")
|
||||
for i in range(0, 3):
|
||||
self.monsters = ChooseMonstersUI(
|
||||
pveController, difficulty, True
|
||||
).monsters
|
||||
print(f"{self.monsters[i].name} join the room")
|
||||
|
||||
monster_name = [monster.name for monster in self.monsters]
|
||||
print(f"Here your new ennemis: {(", ".join(monster_name)).strip()}")
|
||||
|
||||
while self.heroes and self.monsters:
|
||||
self.fight(self.heroes, self.monsters)
|
||||
|
||||
for monster in self.monsters:
|
||||
if monster.HP < 0:
|
||||
self.monsters.remove(monster)
|
||||
|
||||
if self.monsters:
|
||||
self.fight(self.monsters, self.heroes, True)
|
||||
|
||||
for hero in self.heroes:
|
||||
if hero.HP < 0:
|
||||
self.heroes.remove(hero)
|
||||
|
||||
if self.heroes:
|
||||
print("Congrats ! You passed next room")
|
||||
elif self.monsters:
|
||||
print(f"You {fg.red}lose{fg.rs}")
|
||||
self.pveController.mController.close_game()
|
||||
else:
|
||||
raise Exception("An error occured")
|
||||
|
||||
def fight(
|
||||
self,
|
||||
heroes: list[Hero] | list[Monster],
|
||||
opponents: list[Hero] | list[Monster],
|
||||
is_ai_turn: bool = False,
|
||||
):
|
||||
heroes_list_len = len(heroes)
|
||||
|
||||
if heroes_list_len > 1:
|
||||
choice = -1
|
||||
while choice < 1 or choice > heroes_list_len:
|
||||
if is_ai_turn:
|
||||
choice = randint(1, heroes_list_len + 1)
|
||||
else:
|
||||
print("Who will attack?")
|
||||
for i in range(0, heroes_list_len):
|
||||
hero = heroes[i]
|
||||
print(
|
||||
f"{i+1} - {hero.name} | {hero.attack_power} {fg.red}Att{fg.rs} | {hero.defense} {fg.li_magenta}Blo{fg.rs} | {hero.HP}{fg.green} HP{fg.rs}"
|
||||
)
|
||||
|
||||
try:
|
||||
choice = int(input("Give your choice: "))
|
||||
except ValueError:
|
||||
print(f"{fg.red}Input a valid number{fg.rs}")
|
||||
continue
|
||||
|
||||
if choice < 1 or choice > heroes_list_len:
|
||||
print(f"{fg.red}Invalid choice. Try again{fg.rs}")
|
||||
|
||||
choosen_hero = heroes[choice - 1]
|
||||
print(f"{choosen_hero.name} will attack")
|
||||
else:
|
||||
choosen_hero = heroes[0]
|
||||
|
||||
opponents_list_len = len(opponents)
|
||||
|
||||
choice = -1
|
||||
while choice < 1 or choice > opponents_list_len:
|
||||
if is_ai_turn:
|
||||
choice = randint(1, opponents_list_len + 1)
|
||||
else:
|
||||
print("Who do you want to attack?")
|
||||
for i in range(0, opponents_list_len):
|
||||
opponent = opponents[i]
|
||||
print(
|
||||
f"{i+1} - {opponent.name} | {opponent.attack_power} {fg.red}Att{fg.rs} | {opponent.defense} {fg.li_magenta}Blo{fg.rs} | {opponent.HP} {fg.green}HP{fg.rs}"
|
||||
)
|
||||
|
||||
try:
|
||||
choice = int(input("Give your choice: "))
|
||||
except ValueError:
|
||||
print(f"{fg.red}Input a valid number{fg.rs}")
|
||||
continue
|
||||
|
||||
if choice < 1 or choice > opponents_list_len:
|
||||
print(f"{fg.red}Invalid choice. Try again{fg.rs}")
|
||||
|
||||
choosen_opponent = opponents[choice - 1]
|
||||
print(f"{choosen_hero.name} attacks {choosen_opponent.name}")
|
||||
|
||||
self.attacks(choosen_hero, choosen_opponent, is_ai_turn)
|
||||
|
||||
def attacks(self, hero: Hero, opponent: Monster, is_ai_turn: bool):
|
||||
|
||||
attacks_list = hero.attacks_choice
|
||||
len_attack_list = len(attacks_list)
|
||||
|
||||
choice = -1
|
||||
while choice < 1 or choice > len_attack_list:
|
||||
if is_ai_turn:
|
||||
choice = randint(1, len_attack_list + 1)
|
||||
else:
|
||||
print("Choose an attack")
|
||||
for i in range(0, len_attack_list):
|
||||
print(f"{i+1} - {attacks_list[i]}")
|
||||
|
||||
try:
|
||||
choice = int(input("Give your choice: "))
|
||||
except ValueError:
|
||||
print(f"{fg.red}Input a valid number{fg.rs}")
|
||||
continue
|
||||
|
||||
if choice < 1 or choice > len_attack_list:
|
||||
print(f"{fg.red}Invalid choice. Try again{fg.rs}")
|
||||
|
||||
print(f"You choose {attacks_list[choice-1]}")
|
||||
|
||||
if choice == 1:
|
||||
opponent.HP -= hero.attacks(opponent)
|
||||
elif choice == 2:
|
||||
hero.HP = hero.cure()
|
||||
|
||||
if opponent.HP > 0:
|
||||
print(f"{opponent.name}: {opponent.HP} {fg.green}HP{fg.rs}")
|
||||
else:
|
||||
print(f"{opponent.name} is {fg.red}dead{fg.rs}")
|
||||
84
views/PVP/arenaUI.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# arenaUI
|
||||
|
||||
# models
|
||||
from models.heroes.hero import Hero
|
||||
|
||||
from sty import fg
|
||||
|
||||
from random import randint
|
||||
|
||||
# pour éviter les pb d'import circulaire
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from controllers.PvpController import PvpController
|
||||
|
||||
|
||||
class ArenaUI:
|
||||
def __init__(self, pvpController: "PvpController") -> None:
|
||||
self.pvpController = pvpController
|
||||
self.hero = self.pvpController.hero
|
||||
self.opponent = self.pvpController.opponent
|
||||
|
||||
self.init()
|
||||
|
||||
self.pvpController.hero = self.hero
|
||||
|
||||
def init(self):
|
||||
print("You go into the arena")
|
||||
print(f"Are you {fg.green} ready {fg.rs} ?")
|
||||
print("Anyway, no choice ^^")
|
||||
|
||||
print(f"You will {fg.red} fight {fg.red}against{self.opponent.name}")
|
||||
|
||||
if self.hero.type == Hero.ARCHERY:
|
||||
print("You begin")
|
||||
self.attacks(self.hero, self.opponent)
|
||||
else:
|
||||
print(f"{self.opponent.name} begin")
|
||||
|
||||
self.fight()
|
||||
|
||||
def fight(self):
|
||||
while self.hero.HP > 0 and self.opponent.HP > 0:
|
||||
self.attacks(self.hero, self.opponent)
|
||||
|
||||
if self.opponent.HP > 0:
|
||||
self.attacks(self.opponent, self.hero, True)
|
||||
|
||||
if self.hero.HP > 0:
|
||||
print(f"You {fg.blue}won{fg.rs} !")
|
||||
elif self.opponent.HP > 0:
|
||||
print(f"{self.opponent.name} {fg.red}beat{fg.rs} you")
|
||||
else:
|
||||
raise Exception("An error occured")
|
||||
|
||||
def attacks(self, hero: Hero, opponent: Hero, is_AI: bool = False):
|
||||
print(f"Choose an attack - {hero.name} will ...")
|
||||
|
||||
attacks_list = hero.attacks_choice
|
||||
attacks_list_len = len(attacks_list)
|
||||
|
||||
choice = -1
|
||||
while choice < 1 or choice > attacks_list_len:
|
||||
for i in range(0, attacks_list_len):
|
||||
print(f"{i+1} - {attacks_list[i]}")
|
||||
|
||||
try:
|
||||
if is_AI:
|
||||
choice = randint(1, attacks_list_len + 1)
|
||||
else:
|
||||
choice = int(input("Give your choice: "))
|
||||
except ValueError:
|
||||
print(f"{fg.red}Input a valid number{fg.rs}")
|
||||
continue
|
||||
|
||||
if choice < 1 or choice > attacks_list_len:
|
||||
print(f"{fg.red}Invalid choice. Try again{fg.rs}")
|
||||
|
||||
print(f"You choose {attacks_list[choice-1]}")
|
||||
|
||||
if choice == 1:
|
||||
opponent.HP -= hero.attacks(opponent)
|
||||
elif choice == 2:
|
||||
hero.cure()
|
||||
123
views/PVP/chooseCharacterUI.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
# chooseCharacterUI
|
||||
|
||||
from models.loots.armor import Armor
|
||||
from models.loots.weapon import Weapon
|
||||
|
||||
from sty import fg
|
||||
|
||||
from random import randint
|
||||
|
||||
# pour éviter les pb d'import circulaire
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from controllers.PvpController import PvpController
|
||||
|
||||
|
||||
class ChooseCharacterUI:
|
||||
def __init__(self, pvpController: "PvpController", is_AI: bool) -> None:
|
||||
self.pvpController = pvpController
|
||||
self.is_AI = is_AI
|
||||
|
||||
self.hero = None
|
||||
|
||||
self.set_character()
|
||||
self.set_weapon()
|
||||
self.set_armor()
|
||||
|
||||
def set_character(self):
|
||||
heroes = self.pvpController.db.hero_db.get_all_heroes()
|
||||
len_heroes = len(heroes)
|
||||
|
||||
print("Choose your character")
|
||||
|
||||
choice = -1 # Initialiser à une valeur qui n'est pas un index valide
|
||||
while choice < 1 or choice > len_heroes:
|
||||
for i in range(0, len_heroes):
|
||||
hero = heroes[i]
|
||||
print(f"{i+1}. {hero.get_type_str()} - {hero.name}")
|
||||
|
||||
try:
|
||||
if self.is_AI:
|
||||
choice = randint(1, len_heroes)
|
||||
else:
|
||||
choice = int(input("Give your choice: "))
|
||||
except ValueError:
|
||||
print(f"{fg.red}Input a valid number{fg.rs}")
|
||||
continue
|
||||
|
||||
if choice < 1 or choice > len_heroes:
|
||||
print(f"{fg.red}Invalid choice. Try again {fg.rs}")
|
||||
|
||||
# L'indice dans la liste est `choice - 1`
|
||||
# car choice commence à partir de 1
|
||||
self.hero = heroes[choice - 1]
|
||||
self.hero.name = fg.da_magenta + self.hero.name + fg.rs
|
||||
|
||||
print(f"You choose {self.hero.name}!")
|
||||
|
||||
def set_weapon(self):
|
||||
weapons: list[Weapon] = self.pvpController.db.weapon_db.get_weapon_by_hero_type(
|
||||
self.hero
|
||||
)
|
||||
len_weapons = len(weapons)
|
||||
|
||||
print("Choose your weapon now")
|
||||
|
||||
choice = -1
|
||||
while choice < 1 or choice > len_weapons:
|
||||
for i in range(0, len_weapons):
|
||||
weapon = weapons[i]
|
||||
print(f"{i+1}. {weapon.name} - {weapon.description}")
|
||||
|
||||
try:
|
||||
if self.is_AI:
|
||||
choice = randint(0, len_weapons)
|
||||
else:
|
||||
choice = int(input("Give your choice: "))
|
||||
except ValueError:
|
||||
print(f"{fg.red} Input a valid number {fg.rs}")
|
||||
continue
|
||||
|
||||
if choice < 1 or choice > len_weapons:
|
||||
print(f"{fg.red}Invalid choice. Try again {fg.rs}")
|
||||
|
||||
# L'indice dans la liste est `choice - 1`
|
||||
# car choice commence à partir de 1
|
||||
self.hero.weapon = weapons[choice - 1]
|
||||
self.hero.weapon.name = fg.da_magenta + self.hero.weapon.name + fg.rs
|
||||
|
||||
print(f"You choose the weapon: {self.hero.weapon.name}")
|
||||
|
||||
def set_armor(self):
|
||||
armors: list[Armor] = self.pvpController.db.armor_db.get_armor_by_hero_type(
|
||||
self.hero
|
||||
)
|
||||
len_armors = len(armors)
|
||||
|
||||
print("Now, choose your armor")
|
||||
|
||||
choice = -1
|
||||
while choice < 1 or choice > len_armors:
|
||||
for i in range(0, len_armors):
|
||||
armor = armors[i]
|
||||
print(f"{i+1}. {armor.name}")
|
||||
|
||||
try:
|
||||
if self.is_AI:
|
||||
choice = randint(1, len_armors)
|
||||
else:
|
||||
choice = int(input("Give your choice: "))
|
||||
except ValueError:
|
||||
print(f"{fg.red}Input a valid number{fg.rs}")
|
||||
continue
|
||||
|
||||
if choice < 1 or choice > len_armors:
|
||||
print(f"{fg.red}Invalid choice. Try again{fg.rs}")
|
||||
|
||||
# L'indice dans la liste est `choice - 1`
|
||||
# car choice commence à partir de 1
|
||||
self.hero.armor = armors[choice - 1]
|
||||
self.hero.armor.name = fg.da_magenta + self.hero.armor.name + fg.rs
|
||||
|
||||
print(f"You choose {self.hero.armor.name}")
|
||||