Last active
November 23, 2022 23:48
-
-
Save Pinacolada64/7fec32fba9a8fca08ae0fc9bf9bbee56 to your computer and use it in GitHub Desktop.
Getting object classes working.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
A note about positional parameters: | |
https://www.reddit.com/r/learnpython/comments/yptsyp/comment/ivkydz0/?utm_source=share&utm_medium=web2x&context=3 | |
""" | |
import random | |
from datetime import datetime | |
from dataclasses import dataclass | |
from typing import Tuple | |
class Character(object): | |
def __init__(self, **kwargs): | |
self.name = kwargs['name'] | |
self.gender = kwargs['gender'] | |
# print(f"{kwargs['hit_points']=}") | |
x = int(kwargs['hit_points']) | |
# self.hit_points = dict(self.name, kwargs['hit_points']) | |
self.hit_points = kwargs['hit_points'] | |
# self.food = list(kwargs['food']) | |
self.silver = dict(kwargs['silver']) | |
self.inv = list(kwargs['inv']) | |
self.allies = list(kwargs['allies']) | |
self.stat = dict(kwargs['stat']) | |
self.age = kwargs['age'] | |
self.char_class = kwargs['char_class'] | |
self.char_race = kwargs['char_race'] | |
self.flag = dict(kwargs['flag']) | |
print(f'{self.name} {self.flag=}') | |
def display_stats(self): | |
# https://datagy.io/python-type-isinstance/ | |
if isinstance(self, Character): | |
print(f"[{self.name} is a Character.]") | |
print(f" Name: {self.name:17}Gender: {self.gender.title()}") | |
print(f"Class: {self.char_class.title():19}Race: {self.char_race.title()}") | |
for stat_name, stat in self.stat.items(): | |
print(f" {stat_name.title()}: {stat: 2d} ", end='') # end='' | |
print(f"HP: {self.hit_points:,}") | |
total = 0 | |
for kind in ['in_hand', 'in_bank', 'in_bar']: | |
total += self.silver[kind] | |
self.display_silver(kind, right_justify=True) | |
print(f" Total silver: {total:>11,}") | |
# Silver in bank: nnn,nnn,nnn | |
# Total silver: nnn,nnn,nnn | |
if isinstance(self, Monster): | |
print("(TODO: is Monster...)") | |
print(f' []{"-=" * 20}-[]') | |
def display_inv(self): | |
print("\nInventory:") | |
# display silver in hand | |
self.display_silver('in_hand', right_justify=True) | |
if len(self.inv): | |
count = 0 | |
for item in self.inv: | |
count += 1 | |
print(f'{count: 2}. - {item.name}') | |
print(f'\t{item.description}') | |
if isinstance(item, Wand): | |
print("Charges.......Cast %") | |
print(f' {item.charges: 3}{"." * 9}{item.effectiveness: 3}%') | |
if isinstance(item, Book): | |
print("[Book book book book, yep yep yep yep yep uh huh!]") | |
else: | |
print("\tNothing") | |
print("\nAllies:") | |
if self.allies: | |
count = 0 | |
for a in self.allies: | |
count += 1 | |
print(f" {count: 2}. - {a.name:20}HP: {a.hit_points}") | |
if isinstance(a, Monster): | |
if a.flags: | |
print(f' Flags: {a.flags}') | |
else: | |
print("\tNone... sniff...") | |
def display_silver(self, kind: str, right_justify=False): | |
""" | |
:param right_justify: True=right-justify output | |
:param kind: in_hand, in_bank, in_bar | |
:return: "Silver [in hand | in bank | in bar]: value or "None" | |
""" | |
s = self.silver[kind] | |
# replace "_" with " " to transform e.g., "in_hand" -> "in hand": | |
display = kind.replace("_", " ") | |
if s: | |
if right_justify is False: | |
print(f'Silver {display}: {f"{s:,}" if s else "None"}') | |
else: | |
print(f'Silver {display:<7}: {f"{s:>11,}" if s else "None"}') | |
class Monster: | |
def __init__(self, **kwargs): | |
"""info about monsters""" | |
self.name = kwargs['name'] | |
self.hit_points = kwargs['hit_points'] | |
self.flags = kwargs['flags'] | |
self.alignment = kwargs['alignment'] | |
@dataclass | |
class DataclassCharacter: | |
def __init__(self): | |
""" | |
Attributes, flags and other stuff about characters. | |
This apparently does not work with Player.jsonload(**lh_data) | |
from server. | |
""" | |
name: str | |
id: int # for Player.connect | |
connection_id: int # TODO: eventually, CommodoreServer Internet Protocol connection ID | |
gender: str # [ male | female ] | |
# creates a new kwargs dict for each Character: | |
# set with Character.set_stat('xyz', val) | |
stats: dict['chr': int, 'con': int, 'dex': int, 'int': int, 'str': int, 'wis': int, 'egy': int] | |
# status flags: | |
flag: dict[ | |
# status flags: | |
'room_descriptions': bool, | |
'autoduel': bool, | |
'hourglass': bool, | |
'expert': bool, | |
'more_prompt': bool, | |
'architect': bool, | |
# orator_mode: bool # TODO: define orator_mode more succinctly | |
# health flags: | |
'hungry': bool, | |
'thirsty': bool, | |
'diseased': bool, | |
'poisoned': bool, | |
'tired': bool, | |
'on_horse': bool, | |
'unconscious': bool, | |
# other flags: | |
'debug': bool, | |
'dungeon_master': bool, | |
'compass_used': bool, | |
'thug_attack': bool, | |
# game objectives: | |
'spur_alive': bool, | |
'dwarf_alive': bool, | |
'wraith_king_alive': bool, | |
'wraith_master': bool, | |
'tut_treasure': dict['examined': bool, 'taken': bool], | |
# magic items: | |
'gauntlets_worn': bool, | |
'ring_worn': bool, | |
'amulet_of_life': bool, | |
# wizard_glow stuff: | |
# 0 if inactive | |
# != 0 is number of rounds left, decrement every turn | |
'wizard_glow': int | |
] | |
""" | |
# things you can only do once per day (file_formats.txt) | |
'pr' has PRAYed once | |
'pr2' can PRAY twice per day (only if char_class is Druid) | |
""" | |
once_per_day: list | |
# TODO: money types may be expanded to platinum, electrum in future | |
# creates a new silver dict for each Character: | |
# in_bank: may be cleared on character death (TODO: look in TLOS source) | |
# in_bar: should be preserved after character's death (TODO: same) | |
# use Character.set_silver("kind", value) | |
silver: dict['in_hand': int, 'in_bank': int, 'in_bar': int] | |
age: int | |
birthday: Tuple[('0', '0', '0')] # (month, day, year) | |
guild: str # [civilian | fist | sword | claw | outlaw] | |
# 1 2 3 4 5 6 7 8 9 | |
char_class: str # Wizard Druid Fighter Paladin Ranger Thief Archer Assassin Knight | |
race: str # ......Human Ogre Pixie Elf Hobbit Gnome Dwarf Orc Half-Elf | |
natural_alignment: str # good | neutral | evil (depends on race) | |
# client info: | |
client: dict[ | |
# host (i.e., Python, C64, C128...?) | |
'name': None, | |
# screen dimensions: | |
'rows': int, | |
'cols': int, | |
# {'translation': None | ASCII | ANSI | Commodore } | |
'translation': str, | |
# colors for [bracket reader] text highlighting on C64/128: | |
'text': None, | |
'highlight': None, | |
'background': None, | |
'border': None] | |
hit_points: int | |
experience: int | |
# map stats: | |
map_level: int # cl | |
map_room: int # cr | |
moves_made: int | |
# combat stats: | |
armor: list # e.g., should it be its own class with attributes? | |
# Armor(object): | |
# def __init__(name, percent_left, armor_class, ...) | |
# TODO: weight (iron armor vs. padded leather armor will be different), | |
# could also define effectiveness, heavier armor absorbs more damage | |
shield: dict | |
shield_used: int # shield item being USEd | |
shield_skill: dict['item': int, 'skill': int] | |
# same: | |
# Shield(object): | |
# def __init__(name, percent_left, shield_size, ...) | |
# TODO: weight (iron shield vs. wooden shield will be different), | |
# could also define effectiveness, heavier shields absorb more damage | |
weapon: dict | |
weapon_used: int # if != 0, this weapon READYed | |
weapon_skill: dict # {weapon_item: int, weapon_skill: int} | |
weapon_left: int # map this to a rating | |
# bad_hombre_rating is calculated from stats, not stored in player log | |
honor_rating: int # helps determine current_alignment | |
formal_training: int | |
monsters_killed: int | |
""" | |
monsters_killed is not always the same as dead_monsters[]; | |
still increment it if you re-kill a re-animated monster | |
""" | |
dead_monsters: list # keeps track of monsters for Zelda in the bar to resurrect | |
monster_at_quit: str | |
# ally stuff: | |
allies: list # (list of tuples?) | |
ally_inv: list | |
ally_abilities: list | |
# horse stuff: | |
has_horse: bool | |
horse_name: str | |
horse_armor: dict | |
has_saddlebags: bool | |
saddlebags: list # these can carry items for GIVE and TAKE commands | |
vinny_loan: dict # {amount_payable: int, days_til_due: int} | |
# inventory | |
""" | |
# TODO: There should be methods here for Inventory: | |
Inventory.item_held(item): check player/ally inventory, return True or False | |
(is it important to know whether the player or ally is carrying an item?) | |
maybe return Character or Ally object if they hold it, or None if no-one holds it | |
Or could be written: | |
if 'armor' in Character.inventory and 'armor' in Character.used: | |
# meaning 'armor' is in 'inventory' and 'used' lists? | |
# could this be shortened? perhaps: | |
# if Character.ItemHeldUsed('armor') | |
""" | |
max_inv: int | |
# also see weapons[], armor[], shields[] | |
food: list | |
drink: list | |
spells: list # list of dicts('spell_name': str, 'charges', 'chance_to_cast': int) | |
booby_traps: dict['room': int, 'combination': str] # combo: '[a-i]' | |
# TODO: increment at Character.save() | |
times_played: int | |
last_play_date: Tuple[(0, 0, 0)] # (month, day, year) like birthday | |
special_items: dict | |
# SCRAP OF PAPER is randomly placed on level 1 with a random elevator combination | |
# TODO: avoid placing objects in map "holes" where no room exists | |
# DINGHY # does not actually need to be carried around in inventory, I don't suppose, just a flag? | |
combinations: dict['elevator': (0, 0, 0), | |
'locker': (0, 0, 0), | |
'castle': (0, 0, 0)] # tuple: combo is 3 digits: (nn, nn, nn) | |
def create_character(self): | |
pass | |
def display_stats(self: Character): | |
print(f"Name: {self.name:20} Gender: {self.gender.title()}") | |
for stat_name, stat in self.stat.items(): | |
print(f" {stat_name.title()}: {stat: 2d}") # end='' | |
print(f"HP: {self.hit_points:,}") | |
class Item: | |
def __init__(self, name, description): | |
self.name = name | |
self.description = description | |
class Wand(Item): | |
def __init__(self, name, description, charges, attribute, adjustment, effectiveness): | |
# name is already given in Item superclass, but AFAIK can't be left out here? | |
super().__init__(name, description) | |
# self.name = name | |
# self.description = description # shown in inv listing | |
self.charges = charges # how many shots this thing fires | |
self.attribute = attribute # which attribute it affects | |
self.adjustment = adjustment # how many points +/- it affects | |
self.effectiveness = effectiveness # random percentage it succeeds or fails | |
class Book(Item): | |
def __init__(self, name, description, text, attribute, adjustment, effectiveness): | |
super().__init__(name, description) | |
# self.name = name | |
# self.description = description # shown in inv listing | |
self.text = text # text displayed when read | |
self.attribute = attribute # which attribute it affects | |
self.adjustment = adjustment # how many points +/- it affects | |
self.effectiveness = effectiveness # % chance to adjust stats | |
# class Dog(pet): | |
# def __init__(self, name, chases_cats): | |
# pet.__init__(self, name, "Dog") | |
# self.chases_cats = chases_cats | |
# | |
# def ChasesCats(self): | |
# return self.chases_cats | |
guide = Book(name="Adventurer's Guide", | |
description="A guide to adventuring.", | |
text="text", | |
attribute="wis", | |
adjustment=5, | |
effectiveness=50) | |
wand = Wand(name="Wand of Understanding", | |
description="A short stout rod, inscribed 'Clue by four'. " | |
"Intelligence booster.", | |
charges=20, attribute="int", | |
adjustment=5, | |
effectiveness=100) | |
snrak = Item(name="Snrakity Thing", | |
description="A thing of snraking.") | |
toto = Monster(name="Toto", | |
description="A cute small dog.", | |
hit_points=random.randrange(25, 55), | |
flags=['living', 'reanimate'], | |
alignment='good' | |
) | |
""" | |
Amber = Character() | |
Amber.name = "Amber" | |
Amber.hit_points = 100 | |
Amber.silver = {'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000} | |
Amber.inv = [guide] | |
Amber.allies = ['Rulan', 'Jee', 'Shaia'] | |
Amber.stat = {'int': 20, 'wis': 20} | |
Amber.age = 25 | |
Amber.gender = 'female' | |
print(f'Amber.gender=') | |
Rulan = Character() | |
Rulan.name = "Rulan" | |
Rulan.hit_points = 100 | |
Rulan.silver = {'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000} | |
Rulan.inv = [] | |
Rulan.allies = ['Jee'] | |
Rulan.stat = {'int': 5} | |
Rulan.age = 46 | |
Rulan.gender = 'male' | |
Rulan.char_class = 'polarfuchs' | |
Tricuspa = Character() | |
Tricuspa.name = "Tria" | |
Tricuspa.hit_points = 100 | |
Tricuspa.silver = {'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000} | |
Tricuspa.inv = [guide, wand] | |
Tricuspa.allies = list | |
Tricuspa.stat = {'int': 5} | |
Tricuspa.age = 40 | |
Tricuspa.gender = 'female' | |
Jee = Character() | |
Jee.name = "J'ee" | |
Jee.char_class = 'galaxy' | |
Jee.char_race = 'drgn' | |
Jee.hp = 100 | |
Jee.silver = {'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000} | |
Jee.inv = [guide, wand, snrak] | |
Jee.allies = [Rulan] | |
Jee.stat = {'int': 18} | |
Shaia = Character() | |
Shaia.name = "Shaia" | |
Shaia.char_class = "faerie" | |
Shaia.hp = 2000 | |
Shaia.silver = {'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000} | |
Shaia.inv = [wand] | |
Shaia.allies = ["Jee", "Rulan"] | |
Shaia.stat = {'int': 24} | |
Shaia.flag = dict['dungeon_master': True] | |
""" | |
def other_party_members(exclude: list): | |
# generate list of allies from party[], excluding name 'exclude' | |
# expected results: | |
# make_ally_list(exclude="J'ee"): | |
# ['Amber', 'Rulan', 'Shaia'] | |
# result = ['{:#04x}'.format(x) for x in range(256) if x % 2 == 0] | |
# doesn't work: result = [name for name in party if exclude in party] | |
# FIXME: adds excluded party member | |
other_members = [] | |
for char in party: | |
if char in exclude: | |
continue | |
else: | |
other_members.append(char) | |
print(other_members) | |
return other_members | |
Amber = Character(name="Amber", | |
hit_points=100, | |
silver={'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000}, | |
inv=[guide], | |
allies=[], | |
stat={'int': 20, 'wis': 20}, | |
age=25, | |
gender='female', | |
char_race='fennek', | |
char_class='large-eared', | |
flag={'dungeon_master': True, | |
'expert_mode': True} | |
) | |
# add party members as they're instantiated: | |
party = list([Amber]) | |
Amber.allies.append(toto) # Toto is specific to only Amber | |
Rulan = Character(name="Rulan", | |
hit_points=100, | |
silver={'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000}, | |
inv=[guide], | |
allies=[], | |
stat={'int': 20, 'wis': 20}, | |
age=46, | |
gender='male', | |
char_class='fix-it', | |
char_race="polarfuchs", | |
flag={ # status flags: | |
'dungeon_master': True, | |
'room_descriptions': False, | |
'expert_mode': False, | |
'hourglass': True, | |
'autoduel': False, | |
'more_prompt': True, | |
# health flags: | |
'hungry': True, | |
'thirsty': False, | |
'poisoned': False, | |
'diseased': True, | |
'tired': True, | |
'unconscious': False, | |
'architect': False, | |
# orator_mode: False # TODO: define orator_mode more succinctly | |
'has_horse': False, | |
'mounted': False, | |
# other flags: | |
'debug': True, | |
'compass_used': False, | |
'thug_attack': False, | |
# game objectives: | |
'spur_alive': True, | |
'dwarf_alive': True, | |
'wraith_king_alive': True, | |
'wraith_master': False, | |
# magic items: | |
'tut_treasure': {'examined': False, 'taken': False}, | |
'gauntlets_worn': False, | |
'ring_worn': False, | |
'amulet_of_life': False, | |
} | |
) | |
# append party members as they're instantiated: | |
party.append(Rulan) | |
Jee = Character(name="J'ee", | |
hit_points=100, | |
silver={'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000}, | |
inv=[guide, snrak], | |
allies=[], | |
stat={'int': 20, 'wis': 20}, | |
age=45, | |
gender='male', | |
char_class='galaxy', | |
char_race="drgn", | |
flag={'dungeon_master': True, | |
'expert_mode': True} | |
) | |
# append party members as they're instantiated: | |
# we can't use other_party_members() from within the Character instantiation | |
# since they're not all defined yet, so we do it here: | |
party.append(Jee) | |
Shaia = Character(name="Shaia", | |
hit_points=100, | |
silver={'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000}, | |
inv=[guide, wand], | |
allies=[], | |
stat={'int': 20, 'wis': 20}, | |
age=25, | |
gender='female', | |
char_class='fluttery', | |
char_race="fae", | |
flag={'dungeon_master': True, | |
'expert_mode': True} | |
) | |
party.append(Shaia) | |
# add allies to each character from party members, excluding themselves: | |
Rulan.allies = other_party_members(exclude=list(["Rulan"])) | |
Shaia.allies = other_party_members(exclude=list(["Shaia"])) | |
Jee.allies = other_party_members(exclude=list(["Jee"])) | |
""" | |
Rulan = Character() | |
Rulan.name = "Rulan" | |
Rulan.hit_points = 100 | |
Rulan.silver = {'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000} | |
Rulan.inv = [] | |
Rulan.allies = ['Jee'] | |
Rulan.stat = {'int': 5} | |
Rulan.age = 46 | |
Rulan.gender = 'male' | |
Rulan.char_class = 'polarfuchs' | |
Tricuspa = Character() | |
Tricuspa.name = "Tria" | |
Tricuspa.hit_points = 100 | |
Tricuspa.silver = {'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000} | |
Tricuspa.inv = [guide, wand] | |
Tricuspa.allies = list | |
Tricuspa.stat = {'int': 5} | |
Tricuspa.age = 40 | |
Tricuspa.gender = 'female' | |
Jee = Character() | |
Jee.name = "J'ee" | |
Jee.char_class = 'galaxy' | |
Jee.char_race = 'drgn' | |
Jee.hp = 100 | |
Jee.silver = {'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000} | |
Jee.inv = [guide, wand, snrak] | |
Jee.allies = [Rulan] | |
Jee.stat = {'int': 18} | |
Shaia = Character() | |
Shaia.name = "Shaia" | |
Shaia.char_class = "faerie" | |
Shaia.hp = 2000 | |
Shaia.silver = {'in_hand': 2000, 'in_bank': 2000, 'in_bar': 1000} | |
Shaia.inv = [wand] | |
Shaia.allies = ["Jee", "Rulan"] | |
Shaia.stat = {'int': 24} | |
Shaia.flag = dict['dungeon_master': True] | |
""" | |
""" | |
# https://bobbyhadz.com/blog/python-valueerror-update-sequence-element-0-has-length | |
a = dict(name='Alice', age=29) | |
b = {'name': 'Alice', 'age': 29} | |
c = dict(zip(['name', 'age'], ['Alice', 29])) | |
d = dict([('name', 'Alice'), ('age', 29)]) | |
e = dict({'name': 'Alice', 'age': 29}) | |
f = dict({'name': 'Alice'}, age=29) | |
print(a == b == c == d == e == f) # True | |
""" | |
# startup: | |
print("Your status:\n") | |
print(f"Dungeon Master: {Rulan.flag['dungeon_master']}") | |
while True: | |
# print() | |
# parser, such as it is: | |
if Rulan.flag['hourglass'] is True: | |
now = datetime.now() | |
current_time = now.strftime("%I:%M:%S %p") | |
print(f"Time: {current_time} ", end='') | |
if Rulan.flag['expert_mode'] is False: | |
print(f'HP: {Rulan.hit_points} ', end='') | |
print() | |
cmd = input("Command: ").lower() | |
print() # blank line | |
if cmd == "stat": | |
for member in party: | |
Character.display_stats(member) | |
if cmd in ["i", "inv"]: | |
for member in party: | |
Character.display_inv(member) | |
if cmd == "edit": | |
count = 0 | |
for k, v in Rulan.flag.items(): | |
flag = k.replace("_", " ").title() | |
# TODO: some of these flags will read "Yes/No" rather than "On/Off" | |
# add a dict() entry for {"kind": "yes/no"} or {"kind": "on/off"} | |
status = "On" if v is True else "Off" | |
count += 1 | |
print(f'{count: >2}. {flag:.<20}: {status}') | |
print("(Work in progress.)") | |
if cmd in ["q", "quit"]: | |
# real game would ask "are you sure?" | |
exit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Output: