5
\$\begingroup\$

Short (Non-Explanatory) Summary of Code

I just started learning Python a month or 2 ago, and this (small) 955 line mess is what I've worked on since summer started in June. I am basing it on Dungeons and Dragons, and so far, the coding has gone relatively smooth. I used Enthought Canopy to code this, as it allows me to call anything in the code using the input/output box.

So far, I have:

  • A class for NPCs/enemies with methods covering most of the basics.
  • An item class with its own child classes for weapons, armour, and bags.
  • A skill class for attack moves and such.
  • An effect class, which is just for dealing damage and healing so far.
  • An effect-over-time class, which handles the basic damage/heal each turn.
  • A (currently) empty treasure generator, which I'm planning to link to a .txt file.

Explanation of Code

  • An underscore (_) before a variable/method name signifies that it is 'raw'. It's not meant to be accessed by itself or is the backbone of a similarly named method. eg: _inven and inven(), _drop() and drop(). Note that some 'raw' variables and methods do not yet have a non-underscored method.
  • Similar brackets, eg: ((, )), [[, {{, etc. have a space between them to make it easier (for me) to read.
  • Empty lines are added at the ends of if, elif, and else statements. ie:

    if statement:
        #do something
    
    elif otherStatement:
        if moreStatment:
            #do moreSomething
    
        else:
            #do somethingElse
    
    else:
        #do otherSomething
    
  • I also randomly add an empty line whenever it makes it easier (for me) to read.

What to Do

I will accept any and all criticism given to me provided it isn't a blatant stab at my lack of skill. Please include at least 1 Must Change and/or Recommended Change. These can be structure formatting, renaming variables, confused queries of what a method does, etc. I will only accept praise if you hide it within a criticism.

The Code

'''
To Do List:
    * Add _drop() and drop() methods to NPC (in-progress on doc)
        - Convert to suitable format for Bag
    * Create file for treasure
        - Adapt getTreasure() to access file (in-progress)
    * Add Trap
    * Add talk() method to NPC
    * Create GUI (Later)
'''



import math
import random
#import tkinter

class NPC:
    def __init__(self, name='Shrouded Figure', agil=0.0, char=0.0, endur=0.0, stren=0.0, tough=0.0, will=0.0, wis=0.0, luck=0.0, focus=0.0, magpot=0.0, level=None, gold=0, skills={}, inven={}, equipment={}):
        #Name
        self.name = name

        #Attributes
        self._attributes = {
            'agility' : agil, 
            'charisma' : char, 
            'endurance' : endur, 
            'strength' : stren, 
            'toughness' : tough, 
            'willpower' : will, 
            'wisdom' : wis, 
            'luck' : luck, 
            'focus' : focus, 
            'magic potential' : magpot
        }

        #Random attribute generator
        for attrib in self._attributes:   
            if type(self._attributes[attrib]) == list:
                self._attributes[attrib] = float(random.randint(self._attributes[attrib][0], self._attributes[attrib][1]) )

        if type(level) == int:
            for n in range(level):
                self._attributes[random.choice(list(self._attributes) )] += 1.0

        #Stats
        self._stats = {
            'speed' : math.ceil( ( ( (self._attributes['agility'] + ( (self._attributes['agility'] * self._attributes['endurance']) ** 0.5)/3 + self._attributes['endurance']/2) + 1) * (self._attributes['willpower'] + 1) ) ** 0.5), 

            'health' : math.ceil( ( ( self._attributes['toughness']**2 - self._attributes['toughness'])/2 + self._attributes['endurance']/5 + 1) * (self._attributes['willpower']/10 + 1) + 24), 

            'accuracy' : math.ceil( (40*(1 - 0.9**self._attributes['focus']) + 30*(1 - 0.9**self._attributes['luck']) + 20*(1 - 0.9**self._attributes['willpower']) + 10) * 10) / 10, 

            'stamina' : math.ceil( (self._attributes['endurance'] - 1/(self._attributes['willpower'] + 1) + 1) + (self._attributes['willpower'] - 1/(self._attributes['endurance'] + 1) + 1)/2 + 5), 

            'power' : math.ceil( (self._attributes['strength'] + self._attributes['willpower']/2 + (self._attributes['strength']*self._attributes['willpower'] + self._attributes['endurance']/3)/(self._attributes['strength'] + self._attributes['willpower'] + 1) + 1 + self._attributes['endurance']/5) * 10) / 10, 

            'recovery' : math.ceil( (self._attributes['magic potential']/5 + self._attributes['endurance'] + self._attributes['toughness']/3 + 1 + ( (self._attributes['magic potential'] * self._attributes['endurance'] * self._attributes['toughness']) ** 0.5) / 4) / 1.5), 

            'awareness' : math.ceil( ( ( ( (self._attributes['luck']/5 + self._attributes['focus'] + self._attributes['willpower']/3) ** 1.5) + 1.25) / 5) * 10) / 10, 

            'sneaking' : math.ceil( ( ( (self._attributes['luck'] + self._attributes['focus']/2 + self._attributes['agility']/3 + 1) ** 0.75) * (self._attributes['focus']/2 + 1) / 4) * 10) / 10, 

            'dodging' : math.ceil( (50*(1 - 0.9**self._attributes['agility']) + 30*(1 - 0.9**self._attributes['willpower']) + 20*(1 - 0.9**self._attributes['luck']) ) * 10) / 10, 

            'shielding' : math.ceil( (self._attributes['toughness']/2 + self._attributes['willpower']/3 + self._attributes['strength']) * self._attributes['strength']/4), 

            'mPower' : math.ceil( ( ( (self._attributes['magic potential'] + self._attributes['strength']/2 + self._attributes['willpower']/5) * self._attributes['magic potential']/4) ** (1/3) ) * 10) / 10, 

            'mReserves' : math.ceil(self._attributes['magic potential'] + (self._attributes['magic potential'] * self._attributes['endurance'])**0.5 + (self._attributes['endurance']/2)**1.25), 

            'mSkill' : math.ceil( (self._attributes['magic potential'] + self._attributes['wisdom']/2 + (self._attributes['magic potential']*(self._attributes['wisdom'] + 1) ) ) ** 0.5), 

            'mConcentration' : math.ceil( (50*(1 - 0.9**self._attributes['magic potential']) + 30*(1 - 0.9**self._attributes['focus']) + 10*(1 - 0.9**self._attributes['willpower']) + 10*(1 - 0.9**self._attributes['luck']) ) * 10) / 10, 

            'looting' : math.ceil(0.9 * (1 - 0.8**self._attributes['luck'])/0.2 * 20) + 10,

            'capacity' : math.ceil( ( (self._attributes['endurance']/3 + self._attributes['strength'] + 1) * (self._attributes['willpower']/4 + 1) ) + 9)
        }
        '''Unused stats'''
        #'knowledge' : math.ceil( ( ( (self._attributes['charisma']/7 + self._attributes['wisdom'] + self._attributes['magic potential']/11) * (self._attributes['charisma']/5 + self._attributes['wisdom'] + 1) + 1) ** 0.75) * 10) / 10
        #'trading' : math.ceil( (self._attributes['charisma']**2 + self._attributes['wisdom'] + self._attributes['luck']*(self._attributes['luck'] - 1) + 1) ** 0.25)

        #Starting stats
        self.hp = self._stats['health']
        self.sp = self._stats['stamina']
        self.mp = self._stats['mReserves']

        #Skillset
        self._skills = skills

        #Gold
        self.gold = gold

        #Inventory
        self._inven = inven

        #Equipment
        self._equipment = equipment

    #Adjust equality factor
    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    #Adjust string value
    def __str__(self):
        returnStr = 'NPC(name=\'' + self.name + '\''

        if self._attributes['agility'] != 0.0:
            returnStr += ', agil=' + str(self._attributes['agility'])

        if self._attributes['endurance'] != 0.0:
            returnStr += ', endur=' + str(self._attributes['endurance'])

        if self._attributes['charisma'] != 0.0:
            returnStr += ', char=' + str(self._attributes['charisma'])

        if self._attributes['toughness'] != 0.0:
            returnStr += ', tough=' + str(self._attributes['toughness'])

        if self._attributes['strength'] != 0.0:
            returnStr += ', stren=' + str(self._attributes['strength'])

        if self._attributes['wisdom'] != 0.0:
            returnStr += ', wis=' + str(self._attributes['wisdom'])

        if self._attributes['willpower'] != 0.0:
            returnStr += ', will=' + str(self._attributes['willpower'])

        if self._attributes['luck'] != 0.0:
            returnStr += ', luck=' + str(self._attributes['luck'])

        if self._attributes['focus'] != 0.0:
            returnStr += ', focus=' + str(self._attributes['focus'])

        if self._attributes['magic potential'] != 0.0:
            returnStr += ', magpot=' + str(self._attributes['magic potential'])

        if self.gold > 0:
            returnStr += ', gold=' + str(self.gold)

        if len(self._skills) > 0:
            returnStr += ', skills={' + str(self._skills)

            #Used for multiple items
            m = 0
            for skill in self._skills:
                if m > 0:
                    returnStr += ', '

                returnStr += '\'' + skill + '\':' + str(self._skills[skill])
                m += 1

            returnStr += '}'

        if len(self._inven) > 0:
            returnStr += ', inven={' + str(self._inven)

            #Used for multiple items
            m = 0
            for item in self._inven:
                if m > 0:
                    returnStr += ', '

                returnStr += '\'' + item + '\':' + str(self._inven[item])
                m += 1

            returnStr += '}'

        if len(self._equipment) > 0:
            returnStr += ', equipment={'

            #Used for multiple items
            m = 0
            for item in self._equipment:
                if m > 0:
                    returnStr += ', '

                returnStr += '\'' + item + '\':' + str(self._equipment[item])
                m += 1

            returnStr += '}'

        returnStr += ')'

        return returnStr

    #Display info
    def show(self):
        print(self.name)
        print('HP: ' + str(self.hp) + '/' + str(self._stats['health']) )
        print('SP: ' + str(self.sp) + '/' + str(self._stats['stamina']) )
        print('MP: ' + str(self.mp) + '/' + str(self._stats['mReserves']) )
        print('')

    #Activate end-of-turn effects
    def endTurn(self):
        DOT.cycleDOT(target=self)

        self.hp = min(self.hp + self._stats['recovery'], self._stats['health'])
        self.sp = min(self.hp + self._stats['recovery'], self._stats['stamina'])
        self.mp = min(self.hp + self._stats['recovery'], self._stats['mReserves'])

        self.display()

    #Get loot from NPC
    def getLoot(self, user):
        loot = {}

        #Generate loot from inventory
        for item in self._inven:
            #Check for stackable item
            if type(self._inven[item]) == int:
                #Apply looting chances to individual items in stack
                for n in range(self._inven[item]):
                    rand = random.randint(1, 100)
                    if rand <= user._stats['looting']:
                    #Check for/add missing item
                        if item not in loot:
                            loot[item] = 0

                        loot[item] += 1

            else:
                rand = random.randint(1, 100)
                if rand <= user._stats['looting']:
                    loot[item] = self._inven[item]

        #Generate loot from equipment
        for item in self._equipment:
            rand = random.randint(1, 100)
            if rand <= user._stats['looting']:
                loot[self._equipment[item].name] = self._equipment[item]

        #Generate loot from gold
        randG = random.randint(0, self.gold**2)
        randG = randG * (user._stats['looting']/100)
        randG = math.ceil(math.sqrt(randG) )
        loot['Gold'] = randG

        #Generate loot from levels (not affected by looting)
        randXp = 0
        for attrib in self._attributes:
            randXp += math.ceil(self._attributes[attrib] ** 1.5)

        loot['XP'] = randXp

        return loot

    #Get dmg of NPC
    def getDmg(self, mult=1, mod=0):
        #Check for weapon
        if 'weapon' in self._equipment:
            #Differentiate between int and list dmg ranges
            if type(self._equipment['weapon'].dmgRange) == int:
                dmg = math.ceil(mult * self._equipment['weapon'].dmgRange) + mod

            elif type(self._equipment['weapon'].dmgRange) == list:
                dmg = math.ceil(mult * random.randint(self._equipment['weapon'].dmgRange[0], self._equipment['weapon'].dmgRange[1]) ) + mod

        else:
            dmg = math.ceil(mod + mult)

        #Keep dmg above or equal to 0
        dmg = max(0, dmg)

        return dmg

    #Get defence of NPC
    def getDef(self):
        defence = 0
        if 'head' in self._equipment:
            defence += self._equipment['head'].defence

        if 'body' in self._equipment:
            defence += self._equipment['body'].defence

        if 'hands' in self._equipment:
            defence += self._equipment['hands'].defence

        if 'legs' in self._equipment:
            defence += self._equipment['legs'].defence

        if 'feet' in self._equipment:
            defence += self._equipment['feet'].defence

        return defence

    #Add item(s) to inventory
    def give(self, items):
        #Check if items is a dict
        if type(items) == dict:
            for item in items:
                #Item variable
                _item = items[item]

                #Name editor
                nameEdit = ''

                #Re-iterate search algorithm until resolved
                while True:
                    #Check if item is a list w/ an Item instance
                    if type(_item) == list and isinstance(_item[1], Item):
                        #Check if item w/ same name is in inven
                        if (item + nameEdit) in self._inven:
                            #Check if inven item is a list w/ in Item instance
                            if type(self._inven[item + nameEdit]) == list and isinstance(self._inven[item + nameEdit][1], Item):
                                #Check if item and inven item are the same
                                if _item[1] == self._inven[item + nameEdit][1]:
                                    self._inven[item + nameEdit][0] += _item[0]
                                    break

                                else:
                                    #Misfire, add *
                                    nameEdit += '*'

                            #Check if inven item is an int
                            elif type(self._inven[_item[1].name + nameEdit]) == int:
                                #Misfire, add *
                                nameEdit += '*'

                            #Check if item and inven item are the same
                            elif item[1] == self._inven[item[1].name + nameEdit]:
                                self._inven[item + nameEdit] = [_item[0] + 1, _item[1] ]
                                break

                        else:
                            #Add new item
                            self._inven[item + nameEdit] = _item
                            break

                    #Check if item in an int
                    elif type(_item) == int:
                        #Check if item is in inven
                        if (item + nameEdit) in self._inven:
                            #Check if inven item is int
                            if type(self._inven[item + nameEdit]) == int:
                                self._inven[item + nameEdit] += _item
                                break

                            else:
                                #Misfire, add *
                                nameEdit += '*'

                        else:
                            #Add new item
                            self._inven[item + nameEdit] = _item
                            break

                    #Check if item is an Item instance
                    elif isinstance(_item, Item):
                        #Check if item w/ same name is in inven
                        if (item + nameEdit) in self._inven:
                            #Check if inven item is a list w/ in Item instance
                            if type(self._inven[item + nameEdit]) == list and isinstance(self._inven[item + nameEdit][1], Item):
                                #Check if item and inven item are the same
                                if _item == self._inven[item + nameEdit][1]:
                                    self._inven[item + nameEdit][0] += 1
                                    break

                                else:
                                    #Misfire, add *
                                    nameEdit += '*'

                            #Check if inven item is Item instance and the same as item
                            elif isinstance(self._inven[item + nameEdit], Item) and self._inven[item + nameEdit] == _item:
                                self._inven[item + nameEdit] = [2, _item]
                                break

                            else:
                                #Misfire, add *
                                nameEdit += '*'

                        else:
                            #Add new item
                            self._inven[item + nameEdit] = _item
                            break

                    else:
                        print('Item type(?) not available: ' + str(type(_item) ) )
                        break

        #Check if items is a list w/ Item instance
        elif type(items) == list and isinstance(items[1], Item):
            #Name editor
            nameEdit =''

            #Item variable
            _item = items[1]

            while True:
                #Check if item w/ same name is in inven
                if (_item.name + nameEdit) in self._inven:
                    #Check if inven item is a list w/ in Item instance
                    if type(self._inven[_item.name + nameEdit]) == list and isinstance(self._inven[_item.name + nameEdit][1], Item):
                        #Check if item and inven item are the same
                        if _item[1] == self._inven[_item.name + nameEdit][1]:
                            self._inven[_item.name + nameEdit][0] += items[0]
                            break

                        else:
                            #Misfire, add *
                            nameEdit += '*'

                    #Check if item and inven item are the same
                    elif _item == self._inven[_item.name + nameEdit]:
                        self._inven[_item.name + nameEdit] = [items[0] + 1, _item]
                        break

                    else:
                        #Misfire, add *
                        nameEdit += '*'

                else:
                    #Add new item
                    self._inven[_item.name + nameEdit] = items
                    break

        #Check if items is an Item instance
        elif isinstance(items, Item):
            #Name editor
            nameEdit = ''

            #Re-iterate search algorithm until resolved
            while True:
                #Check if item w/ same name is in inven
                if (items.name + nameEdit) in self._inven:
                    #Check if inven item is a list w/ in Item instance
                    if type(self._inven[items.name + nameEdit]) == list and isinstance(self._inven[items.name + nameEdit][1], Item):
                        #Check if item and inven item are the same
                        if items == self._inven[items.name + nameEdit][1]:
                            self._inven[items.name + nameEdit][0] += 1
                            break

                        else:
                            #Misfire, add *
                            nameEdit += '*'

                    #Check if inven item is Item instance and the same as item
                    elif isinstance(self._inven[items.name + nameEdit], Item) and self._inven[items.name + nameEdit] == items:
                        self._inven[items.name + nameEdit] = [2, items]
                        break

                    else:
                        #Misfire, add *
                        nameEdit += '*'
                else:
                    #Add new item
                    self._inven[items.name + nameEdit] = items
                    break

        else:
            print('Items type(?) not available: ' + str(type(items) ) )

        if len(self._inven) > self._stats['capacity']:
            print(self.name + '\'s inventory is overflowing: ' + str(len(self._inven) ) + '/' + str(self._stats['capacity']) )

    #Change stats of NPC
    def editStat(self, stat, newAmount):
        self._stats[stat] = newAmount

        if stat == 'health':
            self.hp = newAmount

        elif stat == 'stamina':
            self.sp = newAmount

        elif stat == 'mReserves':
            self.mp = newAmount

        print(self.name + '\'s ' + stat + " is now " + str(newAmount) )
        print('')

    def recalc(self, reset=False):
        self._stats = {
            'speed' : math.ceil( ( ( (self._attributes['agility'] + ( (self._attributes['agility'] * self._attributes['endurance']) ** 0.5)/3 + self._attributes['endurance']/2) + 1) * (self._attributes['willpower'] + 1) ) ** 0.5), 

            'health' : math.ceil( ( ( self._attributes['toughness']**2 - self._attributes['toughness'])/2 + self._attributes['endurance']/5 + 1) * (self._attributes['willpower']/10 + 1) + 24), 

            'accuracy' : math.ceil( (40*(1 - 0.9**self._attributes['focus']) + 30*(1 - 0.9**self._attributes['luck']) + 20*(1 - 0.9**self._attributes['willpower']) + 10) * 10) / 10, 

            'stamina' : math.ceil( (self._attributes['endurance'] - 1/(self._attributes['willpower'] + 1) + 1) + (self._attributes['willpower'] - 1/(self._attributes['endurance'] + 1) + 1)/2 + 5), 

            'power' : math.ceil( (self._attributes['strength'] + self._attributes['willpower']/2 + (self._attributes['strength']*self._attributes['willpower'] + self._attributes['endurance']/3)/(self._attributes['strength'] + self._attributes['willpower'] + 1) + 1 + self._attributes['endurance']/5) * 10) / 10, 

            'recovery' : math.ceil( (self._attributes['magic potential']/5 + self._attributes['endurance'] + self._attributes['toughness']/3 + 1 + ( (self._attributes['magic potential'] * self._attributes['endurance'] * self._attributes['toughness']) ** 0.5) / 4) / 1.5), 

            'awareness' : math.ceil( ( ( ( (self._attributes['luck']/5 + self._attributes['focus'] + self._attributes['willpower']/3) ** 1.5) + 1.25) / 5) * 10) / 10, 

            'sneaking' : math.ceil( ( ( (self._attributes['luck'] + self._attributes['focus']/2 + self._attributes['agility']/3 + 1) ** 0.75) * (self._attributes['focus']/2 + 1) / 4) * 10) / 10, 

            'dodging' : math.ceil( (50*(1 - 0.9**self._attributes['agility']) + 30*(1 - 0.9**self._attributes['willpower']) + 20*(1 - 0.9**self._attributes['luck']) ) * 10) / 10, 

            'shielding' : math.ceil( (self._attributes['toughness']/2 + self._attributes['willpower']/3 + self._attributes['strength']) * self._attributes['strength']/4), 

            'mPower' : math.ceil( ( ( (self._attributes['magic potential'] + self._attributes['strength']/2 + self._attributes['willpower']/5) * self._attributes['magic potential']/4) ** (1/3) ) * 10) / 10, 

            'mReserves' : math.ceil(self._attributes['magic potential'] + (self._attributes['magic potential'] * self._attributes['endurance'])**0.5 + (self._attributes['endurance']/2)**1.25), 

            'mSkill' : math.ceil( (self._attributes['magic potential'] + self._attributes['wisdom']/2 + (self._attributes['magic potential']*(self._attributes['wisdom'] + 1) ) ) ** 0.5), 

            'mConcentration' : math.ceil( (50*(1 - 0.9**self._attributes['magic potential']) + 30*(1 - 0.9**self._attributes['focus']) + 10*(1 - 0.9**self._attributes['willpower']) + 10*(1 - 0.9**self._attributes['luck']) ) * 10) / 10, 

            'looting' : math.ceil(0.9 * (1 - 0.8**self._attributes['luck'])/0.2 * 20) + 10,

            'capacity' : math.ceil( ( (self._attributes['endurance']/3 + self._attributes['strength'] + 1) * (self._attributes['willpower']/4 + 1) ) + 9)
        }

        if reset:
            #Starting stats
            self.hp = self._stats['health']
            self.sp = self._stats['stamina']
            self.mp = self._stats['mReserves']

        else:
            self.hp = min(self.hp, self._stats['health'])
            self.sp = min(self.sp, self._stats['stamina'])
            self.mp = min(self.mp, self._stats['mReserves'])

    def _levelUp(self,attrib,value=1):
        self._attributes[attrib] +=value
        self.recalc()

    def inven(self):
        if len(self._inven) > 0:
            print(self.name + '\'s inventory:')
            m = 0
            for item in self._inven:
                m += 1
                if isinstance(self._inven[item], Bag):
                    print(str(m) + '/' + str(self._stats['capacity']) + '\t' + item + ':')
                    n = 0
                    for _item in self._inven[item]._inven:
                        n += 1
                        if type(self._inven[item]._inven[_item]) == list:
                            print('\t' + str(n) + '/' + str(self._inven[item].capacity) + '\t' + _item + ': ' + str(self._inven[item]._inven[_item][0]) + ' x ' + str(self._inven[item]._inven[_item][1]) )

                        else:
                            print('\t' + str(n) + '/' + str(self._inven[item].capacity) + '\t' + _item + ': ' + str(self._inven[item]._inven[_item]) )

                elif type(self._inven[item]) == list:
                    print(str(m) + '/' + str(self._stats['capacity']) + '\t' + item + ': ' + str(self._inven[item][0]) + ' x ' + str(self._inven[item][1]) )

                else:
                    print(str(m) + '/' + str(self._stats['capacity']) + '\t' + item + ': ' + str(self._inven[item]) )
        else:
            print(self.name + '\'s inventory is empty.')

#Items
class Item:
    def __init__(self, name, desc=''):
        self.name = name
        self.desc = desc

    def __str__(self):
        returnStr = 'Item(\'' + self.name + '\''

        if len(self.desc) > 0:
            returnStr += ', desc=\'' + self.desc + '\''

        returnStr += ')'

        return returnStr

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

#Weapons
class Weapon(Item):
    def __init__(self, name, kind, dmgRange, minPower=0, mods={}):
        self.name = name
        self.kind = kind
        self.dmgRange = dmgRange
        self.minPower = minPower
        #Currently unused
        self.mods = mods

    #Show weapon info
    def show(self):
        print(self.name)
        print(self.kind)
        #Differentiate between int/list
        if type(self.dmgRange) == list:
            print('Dmg: ' + str(self.dmgRange[0]) + '-' + str(self.dmgRange[1]) )

        else:
            print('Dmg: ' + str(self.dmgRange) )

        if self.minPower > 0:
            print('Minimum Power: ' + str(self.minPower) )

    def __str__(self):
        returnStr = 'Weapon(\'' + self.name + '\', \'' + self.kind + '\', ' + str(self.dmgRange)

        if self.minPower > 0:
            returnStr += ', minPower=' + str(self.minPower)

        if len(self.mods) > 0:
            returnStr += ', mods=' + str(self.mods)

        returnStr += ')'

        return returnStr

#Armour
class Armour(Item):
    def __init__(self, name, place, defence=0, minPower=0, mods={}):
        self.name = name
        self.place = place
        self.defence = defence
        self.minPower = minPower
        #Currently unused
        self.mods = mods

    #Show armour info
    def show(self):
        print(self.name)
        print(self.place)
        print('Defence: ' + str(self.defence) )
        print('Minimum Power: ' + str(self.minPower) )

    def __str__(self):
        returnStr = 'Armour(\'' + self.name + '\', \'' + self.place + '\''

        if self.defence > 0:
            returnStr += ', defence=' + str(self.defence)

        if self.minPower > 0:
            returnStr += ', minPower=' + str(self.minPower)

        if len(self.mods) > 0:
            returnStr += ', mods=' + str(self.mods)

        returnStr += ')'

        return returnStr

class Bag(Item):
    def __init__(self, name, capacity, inven={}):
        self.name = name
        self.capacity = capacity
        self._inven = inven

    def __str__(self):
        returnStr = 'Bag(\'' + self.name + '\', ' + str(self.capacity)

        if len(self._inven) > 0:
            returnStr += ', inven={' + str(self._inven)

            #Used for multiple items
            m = 0
            for item in self._inven:
                if m > 0:
                    returnStr += ', '

                returnStr += '\'' + item + '\':' + str(self._inven[item])
                m += 1

            returnStr += '}'

        returnStr += ')'

        return returnStr

    def inven(self):
        if len(self._inven) > 0:
            print(self.name + ':')
            m = 0
            for item in self._inven:
                m += 1
                if type(self._inven[item]) == list:
                    print(str(m) + '/' + str(self.capacity) + '\t' + item + ': ' + str(self._inven[item][0]) + ' x ' + str(self._inven[item][1]) )

                else:
                    print(str(m) + '/' + str(self.capacity) + '\t' + item + ': ' + str(self._inven[item]) )
        else:
            print(self.name + ' is empty.')

    def add(self,items):
        #Rejected items
        rejects = {}

        #Check if items is a dict
        if type(items) == dict:
            for item in items:
                if len(self._inven) < self.capacity:
                    #Item variable
                    _item = items[item]

                    #Name editor
                    nameEdit = ''

                    #Re-iterate search algorithm until resolved
                    while True:
                        #Check if item is a list w/ an Item instance that is not a Bag
                        if type(_item) == list and isinstance(_item[1], Item) and not isinstance(_item[1],Bag):
                            #Check if item w/ same name is in inven
                            if (item + nameEdit) in self._inven:
                                #Check if inven item is a list w/ in Item instance
                                if type(self._inven[item + nameEdit]) == list and isinstance(self._inven[item + nameEdit][1], Item):
                                    #Check if item and inven item are the same
                                    if _item[1] == self._inven[item + nameEdit][1]:
                                        self._inven[item + nameEdit][0] += _item[0]
                                        break

                                    else:
                                        #Misfire, add *
                                        nameEdit += '*'

                                #Check if inven item is an int
                                elif type(self._inven[_item[1].name + nameEdit]) == int:
                                    #Misfire, add *
                                    nameEdit += '*'

                                #Check if item and inven item are the same
                                elif item[1] == self._inven[item[1].name + nameEdit]:
                                    self._inven[item + nameEdit] = [_item[0] + 1, _item[1] ]
                                    break

                            else:
                                #Add new item
                                self._inven[item + nameEdit] = _item
                                break

                        #Check if item in an int
                        elif type(_item) == int:
                            #Check if item is in inven
                            if (item + nameEdit) in self._inven:
                                #Check if inven item is int
                                if type(self._inven[item + nameEdit]) == int:
                                    self._inven[item + nameEdit] += _item
                                    break

                                else:
                                    #Misfire, add *
                                    nameEdit += '*'

                            else:
                                #Add new item
                                self._inven[item + nameEdit] = _item
                                break

                        #Check if item is an Item instance that is not a Bag
                        elif isinstance(_item, Item) and not isinstance(_item,Bag):
                            #Check if item w/ same name is in inven
                            if (item + nameEdit) in self._inven:
                                #Check if inven item is a list w/ in Item instance
                                if type(self._inven[item + nameEdit]) == list and isinstance(self._inven[item + nameEdit][1], Item):
                                    #Check if item and inven item are the same
                                    if _item == self._inven[item + nameEdit][1]:
                                        self._inven[item + nameEdit][0] += 1
                                        break

                                    else:
                                        #Misfire, add *
                                        nameEdit += '*'

                                #Check if inven item is Item instance and the same as item
                                elif isinstance(self._inven[item + nameEdit], Item) and self._inven[item + nameEdit] == _item:
                                    self._inven[item + nameEdit] = [2, _item]
                                    break

                                else:
                                    #Misfire, add *
                                    nameEdit += '*'

                            else:
                                #Add new item
                                self._inven[item + nameEdit] = _item
                                break

                        else:
                            print('Item type(?) not available: ' + str(type(_item) ) )
                            rejects[str(items)] = items
                            break

                else:
                    rejects[item] = items[item]

        #Check if items is a list w/ Item instance that is not a Bag
        elif type(items) == list and isinstance(items[1], Item) and not isinstance(items[1],Bag):
            #Name editor
            nameEdit =''

            #Item variable
            _item = items[1]

            while True:
                #Check if item w/ same name is in inven
                if (_item.name + nameEdit) in self._inven:
                    #Check if inven item is a list w/ in Item instance
                    if type(self._inven[_item.name + nameEdit]) == list and isinstance(self._inven[_item.name + nameEdit][1], Item):
                        #Check if item and inven item are the same
                        if _item[1] == self._inven[_item.name + nameEdit][1]:
                            self._inven[_item.name + nameEdit][0] += items[0]
                            break

                        else:
                            #Misfire, add *
                            nameEdit += '*'

                    #Check if item and inven item are the same
                    elif _item == self._inven[_item.name + nameEdit]:
                        self._inven[_item.name + nameEdit] = [items[0] + 1, _item]
                        break

                    else:
                        #Misfire, add *
                        nameEdit += '*'

                elif len(self._inven) < self.capacity:
                    #Add new item
                    self._inven[_item.name + nameEdit] = items
                    break

                else:
                    rejects[items[1].name] = items
                    break

        #Check if items is an Item instance that is not a Bag
        elif isinstance(items, Item) and not isinstance(items,Bag):
            #Name editor
            nameEdit = ''

            #Re-iterate search algorithm until resolved
            while True:
                #Check if item w/ same name is in inven
                if (items.name + nameEdit) in self._inven:
                    #Check if inven item is a list w/ in Item instance
                    if type(self._inven[items.name + nameEdit]) == list and isinstance(self._inven[items.name + nameEdit][1], Item):
                        #Check if item and inven item are the same
                        if items == self._inven[items.name + nameEdit][1]:
                            self._inven[items.name + nameEdit][0] += 1
                            break

                        else:
                            #Misfire, add *
                            nameEdit += '*'

                    #Check if inven item is Item instance and the same as item
                    elif isinstance(self._inven[items.name + nameEdit], Item) and self._inven[items.name + nameEdit] == items:
                        self._inven[items.name + nameEdit] = [2, items]
                        break

                    else:
                        #Misfire, add *
                        nameEdit += '*'
                elif len(self._inven) < self.capacity:
                    #Add new item
                    self._inven[items.name + nameEdit] = items
                    break

                else:
                    rejects[items.name] = items
                    break

        else:
            print('Items type(?) not available: ' + str(type(items) ) )
            rejects[str(items)] = items

        if len(rejects) > 0:
            print(self.name + ' is too full, or cannot accept certain items. ' + str(len(rejects) ) + ' items were rejected.')

            return rejects

#Skills
class Skill:
    def __init__(self, name, effects, desc=''):
        self.name = name
        self.effects = effects
        self.desc = desc

    def useSkill(self, user, Target):
        print(user.name + ' used ' + self.name + ' on ' + Target.name)
        if type(self.effects) == list:
            for effect in self.effects:
                eval(effect)

        else:
            eval(self.effects)

    def __str__(self):
        returnStr = 'Skill(\'' + self.name + '\', '

        if type(self.effects) == list:
            returnStr = returnStr + '['

            m = 0
            for effect in self.effects:
                if m > 0:
                    returnStr += ','

                returnStr += '\'' + effect + '\''
                m += 1

            returnStr += ']'

        return returnStr

#Effects
class Effects:
    #Dmg target
    def dealDmg(target, dmg):
        print(target.name + ' received ' + str(dmg) + ' damage')
        target.hp -= dmg
        print(target.name + " " + str(target.hp) + "/" + str(target._stats['health']) )
        print('')

    #Heal target
    def heal(target, health):
        print(target.name + ' received ' + str(health) + ' health')
        target.hp = min(target.hp + health, target._stats['health'])
        print(target.name + " " + str(target.hp) + "/" + str(target._stats['health']) )
        print('')

#Deal dmg/heal over x turns
class DealOverTime:
    def __init__(self):
        self.DOTeffects = []

    #add DOT
    def addDOT(self, target, dmg, turns):
        self.DOTeffects.append([target, dmg, turns])

    #Activate any/all stored DOTs
    def cycleDOT(self, target=None):
        for dot in self.DOTeffects:
            #Check if target is in stored DOTs
            if target == None or dot[0] == target:
                #Check if turns remain
                if dot[2] > 0:
                    #Differentiate between dmg/heal
                    if dot[1] > 0:
                        Effects.dealDmg(dot[0], dot[1])

                    elif dot[1] < 0:
                        Effects.heal(dot[0], -dot[1])

                    #Reduce turn counter
                    dot[2] -= 1

            #Remove DOT w/ 0 remaining turns
            if dot[2] == 0:
                self.DOTeffects.remove(dot)

#Generate treasure
def getTreasure(size):
    pass

#Initialize new DOT storage
DOT = DealOverTime()

#Test stuff
dummy = NPC("Training Dummy", tough=100)
dummy.show()
\$\endgroup\$
  • 3
    \$\begingroup\$ maybe write a library for this program, and break this program into multiple modules... I have never realized 955 lines can be so hard to read for another person. \$\endgroup\$ – hamster on wheels Jul 26 '17 at 3:52
  • \$\begingroup\$ Those nested ifs with gazillion levels is a horror for readers. Break that into multiple functions please ... my head was spinning when I saw that. \$\endgroup\$ – hamster on wheels Jul 26 '17 at 3:54
  • 1
    \$\begingroup\$ Separate calculation (math) and the interface (printing) might be a good idea. When the proof reader/translator tries to work on the text messages, it is not fun if they are all scattered over everywhere in the code. \$\endgroup\$ – hamster on wheels Jul 26 '17 at 3:58
  • \$\begingroup\$ @rxu Thanks for all these tips. Please compile them into a review, with some basic solutions, and I'll get right on them. \$\endgroup\$ – The Eye Jul 27 '17 at 0:30
  • 1
    \$\begingroup\$ With @hamsteronwheels point about a library I would probably put NPC in a file on its own to be imported and then leave the rest there \$\endgroup\$ – 13ros27 Dec 16 '17 at 17:24
2
\$\begingroup\$

Sorry to be the person who does this (I always have this happen when someone reviews my code) but PEP8. The lines between the statements are good but please try and keep your line length under 80 characters. For instance, your init line of NPC is at least three times the recommended line length in PEP8 and later on in init the sums reach even longer. I am not sure what to do for the initialisation apart from maybe have the values set in a few functions you call one after the other (although this seems unnecessary) however the sums should either be separated so that you do some sums on the variable then some more on the next line or just have some line breaks because it helps with readability no end.

Another thing to do with PEP8 is that it may help to add docstrings to the start of your classes and possibly functions as well just explaining briefly what it does (trust me, if you have nothing like that then you will come back to your code a year later and have no idea what the different bits do). It also makes later development a lot easier. Also, with most of your classes you have a comment just before them detailing what they do and it is more pythonesque to put them as the first thing in the class and it also means you can poll the class for it.

I can kind of understand why you have a commented out import tkinter because you plan to add a GUI later but it just seems unnecessary at this stage.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.