initial commit

This commit is contained in:
ExostFlash 2025-03-03 16:47:07 +01:00
parent 661d60c0f7
commit 483bd5299a
43 changed files with 31904 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
build/
env/
__pycache__/
*.db

BIN
Logo/PythonGame (1).jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

BIN
Logo/PythonGame (1).png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 MiB

BIN
Logo/PythonGame.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

BIN
Logo/logo.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

61
README.md Normal file
View 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)_

File diff suppressed because one or more lines are too long

BIN
Sujet/Présentation.pptx Normal file

Binary file not shown.

85
Sujet/README - Real.md Normal file
View 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)_

View 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")

View 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)

View 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

Binary file not shown.

View 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)

View 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

View 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

View 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

View 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
View 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

Binary file not shown.

BIN
database/mdl/V1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
database/mdl/V2.mwb Normal file

Binary file not shown.

BIN
database/mdl/V2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
database/mdl/V3.mwb Normal file

Binary file not shown.

BIN
database/mdl/V3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
dist/Serpent s Legacy.exe vendored Normal file

Binary file not shown.

19
main.py Normal file
View 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
View 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
View 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"

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

View 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
View 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
View 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()

View 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}")