238 lines
9.6 KiB
Python
238 lines
9.6 KiB
Python
# Game imports
|
|
from utils.serializable import Serializable
|
|
from utils.dice import Dice
|
|
from events.event import Event
|
|
from entities.player import Player
|
|
from entities.npc import NPC
|
|
from utils.item import Item
|
|
from events.turn import TurnAction
|
|
|
|
# Native imports
|
|
from enum import IntEnum
|
|
|
|
class Stat(IntEnum):
|
|
STRENGTH = 0
|
|
INTELLIGENCE = 1
|
|
DEXTERITY = 2
|
|
WISDOM = 3
|
|
CHARISMA = 4
|
|
HP = 5
|
|
ARMOR = 6
|
|
SPEED = 7
|
|
|
|
class Game(Serializable):
|
|
def __init__(self, seed:int=42):
|
|
self.active_players:list[Player] = []
|
|
self.active_npcs:list[NPC] = []
|
|
self.active_items:list[Item] = []
|
|
|
|
self.events:list[Event] = []
|
|
self.turn_order:list[str] = []
|
|
self.turn_idx = 0
|
|
|
|
def get_entity(self, entity_id:str):
|
|
for entity in (self.active_players + self.active_npcs):
|
|
if entity.id == entity_id:
|
|
return entity
|
|
raise ReferenceError(f"The player #{entity_id} doesn't exist!")
|
|
|
|
def get_player(self, player_id:str):
|
|
for player in self.active_players:
|
|
if player.id == player_id:
|
|
return player
|
|
raise ReferenceError(f"The player #{player_id} doesn't exist!")
|
|
|
|
def create_player(self, name:str, strength:int, dexterity:int, intelligence:int, wisdom:int, charisma:int, hp:int, armor:int, speed:int, equipped_item:Item):
|
|
new_player = Player(name, strength, dexterity, intelligence, wisdom, charisma, hp, armor, speed, equipped_item)
|
|
self.active_players.append(new_player)
|
|
return new_player.id
|
|
|
|
def get_npc(self, npc_id:str):
|
|
for npc in self.active_npcs:
|
|
if npc.id == npc_id:
|
|
return npc
|
|
raise ReferenceError(f"The npc #{npc_id} doesn't exist!")
|
|
|
|
def create_npc(self, name:str, strength:int, dexterity:int, intelligence:int, wisdom:int, charisma:int, hp:int, armor:int, speed:int, equipped_item:Item):
|
|
new_npc = NPC(name, strength, dexterity, intelligence, wisdom, charisma, hp, armor, speed, equipped_item)
|
|
self.active_npcs.append(new_npc)
|
|
return new_npc.id
|
|
|
|
def get_item(self, item_id:str):
|
|
for item in self.active_items:
|
|
if item.id == item_id:
|
|
return item
|
|
raise ReferenceError(f"The item #{item_id} doesn't exist!")
|
|
|
|
def create_item(self,name:str, description:str, stat_modifier:dict[str, int]):
|
|
new_item = Item(name, description, stat_modifier)
|
|
self.active_items.append(new_item)
|
|
return new_item.id
|
|
|
|
def add_player(self, new_player:Player):
|
|
if new_player.id in [player.id for player in self.active_players]:
|
|
raise ReferenceError(f"Player id #{new_player.id} already present in game!")
|
|
self.active_players.append(new_player)
|
|
|
|
def add_ncp(self, new_ncp:NPC):
|
|
if new_ncp.id in [npc.id for npc in self.active_npcs]:
|
|
raise ReferenceError(f"NCP id #{new_ncp.id} already present in game!")
|
|
self.active_npcs.append(new_ncp)
|
|
|
|
def add_item(self, new_item:Item):
|
|
if new_item.id in [item.id for item in self.active_items]:
|
|
raise ReferenceError(f"Item id #{new_item.id} already present in game!")
|
|
self.active_items.append(new_item)
|
|
|
|
def add_item_to_entity(self, item_id:str, entity_id:str):
|
|
item = self.get_item(item_id)
|
|
entity = self.get_entity(entity_id)
|
|
|
|
entity.set_equipped_item(item)
|
|
self.active_items.remove(item)
|
|
|
|
def get_current_event(self):
|
|
idx = len(self.events) - 1
|
|
if idx < 0:
|
|
raise IndexError("There is no event yet, you should create one!")
|
|
return self.events[idx]
|
|
|
|
def add_event(self, new_event:Event):
|
|
self.events.append(new_event)
|
|
self.update_turn_order()
|
|
self.turn_idx = 0
|
|
|
|
def add_entity_to_event(self, entity_id:str):
|
|
self.get_current_event().add_entity(entity_id=entity_id)
|
|
|
|
def check_turn_ended(self):
|
|
if self.turn_idx == len(self.turn_order)-1:
|
|
# Turn end
|
|
self.get_current_event().add_turn()
|
|
self.update_turn_order()
|
|
|
|
def update_turn_order(self):
|
|
active_entities = self.get_current_event().entities
|
|
entity_list = [self.get_entity(id) for id in active_entities]
|
|
|
|
entity_list = self.sort_entities_by_speed(entity_list)
|
|
|
|
self.turn_order = [entity.id for entity in entity_list]
|
|
self.turn_idx = 0
|
|
|
|
# Selection sort based on entity's speed
|
|
def sort_entities_by_speed(self, entity_list:list[NPC|Player]):
|
|
n = len(entity_list)
|
|
|
|
for i in range(n):
|
|
max_idx = i # Current fastest entity
|
|
for j in range(i+1, n):
|
|
entity_max = entity_list[max_idx]
|
|
entity_j = entity_list[j]
|
|
|
|
if entity_max.speed < entity_j.speed:
|
|
max_idx = j # New Maximum Speed for j entity
|
|
|
|
# Swapping current index i with the new maximum in i+1, n-1
|
|
entity_list[i], entity_list[max_idx] = entity_list[max_idx], entity_list[i]
|
|
|
|
return entity_list
|
|
|
|
def kill_entity(self, entity_id:str):
|
|
dead_entity = self.get_entity(entity_id)
|
|
self.get_current_event().remove_entity(entity_id=entity_id)
|
|
|
|
if isinstance(dead_entity, NPC):
|
|
self.active_npcs.remove(dead_entity)
|
|
if isinstance(dead_entity, Player):
|
|
self.active_players.remove(dead_entity)
|
|
|
|
def is_turn_coherent(self, entity_id:str):
|
|
return self.turn_order[self.turn_idx] == entity_id
|
|
|
|
def deal_damage(self, src:str, target:str, roll:int, stat:int, description:str):
|
|
if not self.is_turn_coherent(src):
|
|
raise ReferenceError(f"Entity #{src} tried performing an action while it was #{self.turn_order[self.turn_idx]}'s turn!")
|
|
|
|
src_entity = self.get_entity(src)
|
|
target_entity = self.get_entity(target)
|
|
|
|
dmg_amount = 0
|
|
if stat == Stat.STRENGTH: # Strength damages physical and long closed range weapons
|
|
dmg_amount += (roll * 5 / target_entity.get_armor()) * src_entity.get_strength()
|
|
elif stat == Stat.INTELLIGENCE: # Using magic
|
|
dmg_amount += (roll * 5 / target_entity.get_armor()) * src_entity.get_intelligence()
|
|
elif stat == Stat.DEXTERITY: # Using daggers, bows or throws
|
|
dmg_amount += (roll * 5 / target_entity.get_armor()) * src_entity.get_dexterity()
|
|
|
|
target_entity.deal_damage(int(dmg_amount))
|
|
|
|
additional_info = f"; {target_entity.name}({target_entity.get_id()}) took {dmg_amount} damage, {target_entity.get_hp()}hp remaining!"
|
|
|
|
if target_entity.get_hp() <= 0:
|
|
self.kill_entity(target_entity.get_id())
|
|
additional_info = f"; {target_entity.name}({target_entity.get_id()}) took {dmg_amount} damage, {target_entity.name} died!"
|
|
|
|
turn_finished = self.get_current_event().perform_action(TurnAction.DAMAGE, src, description=description+additional_info)
|
|
self.turn_idx += 1
|
|
if turn_finished:
|
|
self.update_turn_order()
|
|
return int(dmg_amount)
|
|
|
|
def modifying_stat(self, src:str, value:int, stat:int, description:str):
|
|
if not self.is_turn_coherent(src):
|
|
raise ReferenceError(f"Entity #{src} tried performing an action while it was #{self.turn_order[self.turn_idx]}'s turn!")
|
|
|
|
src_entity = self.get_entity(src)
|
|
match(stat):
|
|
case Stat.STRENGTH:
|
|
src_entity.set_strength(src_entity.strength + value)
|
|
case Stat.INTELLIGENCE:
|
|
src_entity.set_intelligence(src_entity.intelligence + value)
|
|
case Stat.DEXTERITY:
|
|
src_entity.set_dexterity(src_entity.dexterity + value)
|
|
case Stat.WISDOM:
|
|
src_entity.set_wisdom(src_entity.wisdom + value)
|
|
case Stat.CHARISMA:
|
|
src_entity.set_charisma(src_entity.charisma + value)
|
|
|
|
case Stat.HP:
|
|
src_entity.set_hp(src_entity.hp + value)
|
|
case Stat.ARMOR:
|
|
src_entity.set_armor(src_entity.armor + value)
|
|
case Stat.SPEED:
|
|
src_entity.set_speed(src_entity.speed + value)
|
|
|
|
turn_finished = self.get_current_event().perform_action(TurnAction.STATS, src, description=description)
|
|
self.turn_idx += 1
|
|
if turn_finished:
|
|
self.update_turn_order()
|
|
|
|
def simple_action(self, src:str, stat:Stat, difficulty:int, roll:int, description:str):
|
|
if not self.is_turn_coherent(src):
|
|
raise ReferenceError(f"Entity #{src} tried performing an action while it was #{self.turn_order[self.turn_idx]}'s turn!")
|
|
|
|
src_entity = self.get_entity(src)
|
|
stat_boost = 0 # Between 0 and 5
|
|
match(stat):
|
|
case Stat.STRENGTH:
|
|
stat_boost = src_entity.get_strength() / 4
|
|
case Stat.INTELLIGENCE:
|
|
stat_boost = src_entity.get_intelligence() / 4
|
|
case Stat.DEXTERITY:
|
|
stat_boost = src_entity.get_dexterity() / 4
|
|
case Stat.WISDOM:
|
|
stat_boost = src_entity.get_wisdom() / 4
|
|
case Stat.CHARISMA:
|
|
stat_boost = src_entity.get_charisma() / 4
|
|
test_result = roll + stat_boost
|
|
action_performed = difficulty <= test_result
|
|
|
|
additional_info = f", (Test difficulty: {difficulty}, Player roll: {test_result})"
|
|
|
|
turn_finished = self.get_current_event().perform_action(TurnAction.BASIC, src, description=description+additional_info)
|
|
self.turn_idx += 1
|
|
if turn_finished:
|
|
self.update_turn_order()
|
|
|
|
return action_performed, test_result |