8ccecd68 by Barry

Added soul plates, reflectors and messaging for pankration.

1 parent 71937a50
......@@ -247,26 +247,101 @@ def db_get_pankration_record(member_id):
pan_record.close()
return dict_factory(c, result)
def db_add_monster(member_id, monster):
def db_add_soul_plate(member_id, soul_plate):
pan_record = db_get_pankration_record(member_id)
log("{}".format(member_id))
conn = sqlite3.connect('db.sqlite3')
c = conn.cursor()
db_state = c.execute("SELECT monster_inventory FROM pankration WHERE member_id = ?;", (member_id,)).fetchone()
db_state = c.execute("SELECT soul_plates, member_id FROM pankration WHERE member_id = ?;", (member_id,)).fetchone()
if not db_state:
c.execute("""INSERT INTO pankration(member_id, wins, losses, monster_inventory)
VALUES(?, ?, ?, ?);""", (member_id, 0, 0, pickle.dumps([monster])))
log("no record")
c.execute("""INSERT INTO pankration(member_id, wins, losses, soul_plates)
VALUES(?, ?, ?, ?);""", (member_id, 0, 0, pickle.dumps([soul_plate])))
conn.commit()
else:
log(db_state)
inv = str(db_state[0])
log(inv)
monster_inventory = pickle.loads(inv)
monster_inventory.append(monster)
c.execute("""UPDATE pankration SET monster_inventory = ?
WHERE member_id = ?;""", (pickle.dumps(monster_inventory), member_id))
if db_state[0] and len(db_state[0]) > 0:
inv = str(db_state[0])
soul_plates = pickle.loads(inv)
else:
soul_plates = []
soul_plates.append(soul_plate)
c.execute("""UPDATE pankration SET soul_plates = ?
WHERE member_id = ?;""", (pickle.dumps(soul_plates), member_id))
conn.commit()
def db_remove_soul_plate(member_id, idx):
pan_record = db_get_pankration_record(member_id)
log("{}".format(member_id))
conn = sqlite3.connect('db.sqlite3')
c = conn.cursor()
db_state = c.execute("SELECT soul_plates, member_id FROM pankration WHERE member_id = ?;", (member_id,)).fetchone()
if not db_state:
log("Invalid remove request for reflector: {}".format(idx,))
return False
else:
log(db_state)
if db_state[0] and len(db_state[0]) > 0:
inv = str(db_state[0])
soul_plates = pickle.loads(inv)
else:
log("Invalid remove request for reflector: {}".format(idx,))
return False
del soul_plates[idx]
c.execute("""UPDATE pankration SET soul_plates = ?
WHERE member_id = ?;""", (pickle.dumps(soul_plates), member_id))
conn.commit()
def db_add_reflector(member_id, reflector):
pan_record = db_get_pankration_record(member_id)
log("{}".format(member_id))
conn = sqlite3.connect('db.sqlite3')
c = conn.cursor()
db_state = c.execute("SELECT reflectors, member_id FROM pankration WHERE member_id = ?;", (member_id,)).fetchone()
if not db_state:
log("no record")
c.execute("""INSERT INTO pankration(member_id, wins, losses, reflectors)
VALUES(?, ?, ?, ?);""", (member_id, 0, 0, pickle.dumps([reflector])))
conn.commit()
else:
log(db_state)
if db_state[0] and len(db_state[0]) > 0:
inv = str(db_state[0])
reflectors = pickle.loads(inv)
else:
reflectors = []
reflectors.append(reflector)
c.execute("""UPDATE pankration SET reflectors = ?
WHERE member_id = ?;""", (pickle.dumps(reflectors), member_id))
conn.commit()
def db_remove_reflector(member_id, idx):
pan_record = db_get_pankration_record(member_id)
log("{}".format(member_id))
conn = sqlite3.connect('db.sqlite3')
c = conn.cursor()
db_state = c.execute("SELECT reflectors, member_id FROM pankration WHERE member_id = ?;", (member_id,)).fetchone()
if not db_state:
log("Invalid remove request for reflector: {}".format(idx,))
return False
else:
log(db_state)
if db_state[0] and len(db_state[0]) > 0:
inv = str(db_state[0])
reflectors = pickle.loads(inv)
else:
log("Invalid remove request for reflector: {}".format(idx,))
return False
del reflectors[idx]
c.execute("""UPDATE pankration SET reflectors = ?
WHERE member_id = ?;""", (pickle.dumps(reflectors), member_id))
conn.commit()
def db_convert_soul_plate_to_reflector(member_id, soul_plate, idx):
db_add_reflector(member_id, soul_plate)
db_remove_soul_plate(member_id, idx)
def db_get_member(discord_id=None, username=None):
# Do a lookup by ID, if it's found but the name doesn't match then add a row to aliases with the previous name and change the member name
member_conn = sqlite3.connect('db.sqlite3')
......
No preview for this file type
......@@ -85,7 +85,11 @@ registered_commands = {'!help': 'do_help', '!commands': 'do_help',
'!rigged': 'do_rigged',
'!listzones': 'do_list_zones',
'!huntmonster': 'do_hunt_monster',
'!listmonster': 'do_list_monsters',
'!listferalskills': 'do_list_feral_skills', '!listferalskill': 'do_list_feral_skills',
'!listreflectors': 'do_list_reflectors', '!listreflector': 'do_list_reflectors',
'!listsoulplates': 'do_list_soul_plates', '!listsoulplate': 'do_list_soul_plates',
'!convertplate': 'do_convert_plate', '!convertsoulplate': 'do_convert_plate',
'!pankration': 'do_pankration',
}
......@@ -1015,34 +1019,116 @@ def do_rigged(client, message_parts, message):
send_message(client, message.channel, ":musical_note: {} :musical_note:".format(random.choice(lines)))
return
def do_list_monsters(client, message_parts, message):
#### PANKRATION
def do_pankration(client, message_parts, message):
send_message(client, message.channel, """**NOTE! This is NOT implemented yet.. only partially**
{} Available Commands:
*Hunting for Soul Plates:*
**!listzones** - Returns a list of zones available for hunting and how many credits it costs to search in that zone. Depending on the weather and other factors some zones may not always be available.
**!huntmonster <zone>** - An attempt will be made to search for a soul plate (monster) in the zone specified. Each search costs a certain amount of credits. Each zone has a different cost.
*Viewing Your Inventory:*
**!listsoulplates** - Returns a list of all soul plates you have in your posession.
**!listreflectors** - Returns a list of all reflectors you have in your posession (The available monsters you can fight with).
**!listferalskills** - Returns a list of all feral skills you have in your inventory
*Editing Your Collections:*
**!convertplate <plate number> <convert_to>** - From the list of soul plates you can choose to either convert the plate into a \"reflector\" or \"skill\".
Ex: **!convertplate 1 reflector**
**!convertplate 4 skill**
**!assignskill <reflector number> <skill num>** - Assigns a skill to a reflector. Each skill is worth a certain amount of feral points, each monster has a maximum of feral points available.
*Arena Battle:*
**!registerbattle <reflector number>** - Adds your reflector to the queue in the arena. When the arena is available your monster will be paired with either another players monster or a similarly matched arena monster.
**!registerbattle <reflector number> <player name>** - If you want to challenge a specific user to a battle just provide their user name. Have them do the same and your battle will start when the arena is available.
""".format(message.author.mention()))
def do_convert_plate(client, message_parts, message):
member = data.db_get_member(message.author.id)
pankration_data = data.db_get_pankration_record(member['member_id'])
if pankration_data and pankration_data['soul_plates']:
log(message_parts)
if len(message_parts) < 2:
send_message(client, message.channel, 'You must provide at least 2 arguments for converting a plate. The plate number and the type of conversion. Please see !pankration for an example.')
return
plate_num = message_parts[0]
convert_to = message_parts[1]
soul_plates = pickle.loads(str(pankration_data['soul_plates']))
if not plate_num.isdigit() or int(plate_num) < 1 or int(plate_num) > len(soul_plates):
send_message(client, message.channel, 'The requested plate is invalid. Please provide the number from !listsoulplates')
return
plate_num = int(plate_num)
if convert_to == "reflector":
# do reflector stuff
data.db_convert_soul_plate_to_reflector(member['member_id'], soul_plates[plate_num], plate_num)
pass
elif convert_to == "skill":
# do skill stuff
pass
else:
send_message(client, message.channel, 'A plate can only be converted into a reflector or skill. Please see !pankration for an example.')
return
#send_message(client, message.channel, "\n\n".join("{}. {}".format(idx+1, str(soul_plate)) for idx, soul_plate in enumerate(soul_plates)))
else:
send_message(client, message.channel, 'You have no soul plates to convert.')
def do_list_soul_plates(client, message_parts, message):
member = data.db_get_member(message.author.id)
log(member)
monster_data = data.db_get_pankration_record(member['member_id'])
if monster_data:
monsters = pickle.loads(str(monster_data['monster_inventory']))
send_message(client, message.channel, "\n\n".join(str(monster) for monster in monsters))
pankration_data = data.db_get_pankration_record(member['member_id'])
if pankration_data and pankration_data['soul_plates']:
soul_plates = pickle.loads(str(pankration_data['soul_plates']))
send_message(client, message.channel, "\n\n".join("{}. {}".format(idx+1, str(soul_plate.get_soul_plate_description())) for idx, soul_plate in enumerate(soul_plates)))
else:
send_message(client, message.channel, 'You have no monsters.')
send_message(client, message.channel, 'You have no soul plates.')
def do_list_reflectors(client, message_parts, message):
member = data.db_get_member(message.author.id)
pankration_data = data.db_get_pankration_record(member['member_id'])
if pankration_data and pankration_data['reflectors']:
reflectors = pickle.loads(str(pankration_data['reflectors']))
send_message(client, message.channel, "\n\n".join("{}. {}".format(idx+1, str(reflector)) for idx, reflector in enumerate(reflectors)))
else:
send_message(client, message.channel, 'You have no reflectors.')
def do_list_feral_skills(client, message_parts, message):
member = data.db_get_member(message.author.id)
pankration_data = data.db_get_pankration_record(member['member_id'])
if pankration_data and pankration_data['feral_skills']:
feral_skills = pickle.loads(str(pankration_data['feral_skills']))
send_message(client, message.channel, "\n\n".join("{}. {}".format(idx+1, str(feral_skill)) for idx, feral_skill in enumerate(feral_skills)))
else:
send_message(client, message.channel, 'You have no feral skills.')
def do_hunt_monster(client, message_parts, message):
p = Pankration()
hunt_response = p.hunt_monster(' '.join(message_parts))
str_out = hunt_response.message + "\n\n"
if hunt_response.result == HuntResponse.SUCCESS:
monster = hunt_response.monster
soul_plate = hunt_response.monster
member = data.db_get_member(message.author.id)
data.db_add_monster(member['member_id'], monster)
str_out += str(monster)
data.db_add_soul_plate(member['member_id'], soul_plate)
str_out += str(soul_plate.get_soul_plate_description())
send_message(client, message.channel, str_out)
return
def do_list_zones(client, message_parts, message):
p = Pankration()
send_message(client, message.channel, ', '.join(p.list_zones()))
return
### END PANKRATION
def start_timer(client):
needs_loot = True
while not quitting:
......
......@@ -79,6 +79,31 @@ class Jobs:
members = [attr for attr in dir(Jobs()) if not callable(attr) and not attr.startswith("__") and not attr.startswith("get_job_name")]
return members[job-1]
evasion_by_job = {
Jobs.BLM: {'base_eva': 4, 'eva_per_level': 2.48},
Jobs.BLU: {'base_eva': 5, 'eva_per_level': 2.78},
Jobs.BRD: {'base_eva': 4, 'eva_per_level': 2.66},
Jobs.BST: {'base_eva': 5, 'eva_per_level': 2.78},
Jobs.COR: {'base_eva': 4, 'eva_per_level': 2.66},
Jobs.DNC: {'base_eva': 5, 'eva_per_level': 2.88},
Jobs.DRG: {'base_eva': 5, 'eva_per_level': 2.88},
Jobs.DRK: {'base_eva': 5, 'eva_per_level': 2.78},
Jobs.GEO: {'base_eva': 4, 'eva_per_level': 2.66},
Jobs.MNK: {'base_eva': 5, 'eva_per_level': 2.88},
Jobs.NIN: {'base_eva': 6, 'eva_per_level': 3},
Jobs.PLD: {'base_eva': 5, 'eva_per_level': 2.78},
Jobs.PUP: {'base_eva': 5, 'eva_per_level': 2.88},
Jobs.RDM: {'base_eva': 4, 'eva_per_level': 2.66},
Jobs.RNG: {'base_eva': 4, 'eva_per_level': 2.48},
Jobs.RUN: {'base_eva': 5, 'eva_per_level': 2.88},
Jobs.SAM: {'base_eva': 5, 'eva_per_level': 2.88},
Jobs.SCH: {'base_eva': 4, 'eva_per_level': 2.48},
Jobs.SMN: {'base_eva': 4, 'eva_per_level': 2.48},
Jobs.THF: {'base_eva': 6, 'eva_per_level': 3},
Jobs.WAR: {'base_eva': 5, 'eva_per_level': 2.78},
Jobs.WHM: {'base_eva': 4, 'eva_per_level': 2.48},
}
TemperamentPosture = {
4: {"name": "Very Agressive", "message": "Show no mercy!", "value": 4},
3: {"name": "Somewhat Agressive", "message": "Give em' a little more bite!", "value": 3},
......@@ -297,9 +322,28 @@ class Monster:
self.base_dex = family['base_dex']
self.dex_per_level = family['dex_per_level']
self.hp = self.get_hp()
self.wins = 0
self.losses = 0
def __str__(self):
return "**Stats:**\nFamily: {}\nLevel: {}\nMain Job: {}\nSupport Job: {}\nInnate Feral Skills: {}\nEquipped Feral Skills: {}\nFeral Points: {}".format(self.family_name, self.level, Jobs().get_job_name(self.main_job), Jobs().get_job_name(self.support_job), ', '.join(self.innate_feral_skills), ', '.join(self.equipped_feral_skills), self.get_fp())
try:
return "**{}**\n*Stats:*\nFamily: {}\nLevel: {}\nMain Job: {}\nSupport Job: {}\nInnate Feral Skills: {}\nEquipped Feral Skills: {}\nFeral Points: {}\n\nWins: {}\nLosses: {}".format(self.monster_type, self.family_name, self.level, Jobs().get_job_name(self.main_job), Jobs().get_job_name(self.support_job), ', '.join(self.innate_feral_skills), ', '.join(self.equipped_feral_skills), self.get_fp(), self.wins, self.losses)
except AttributeError:
return "Old Format monster, unable to display."
#return "Family: {}\nLevel: {}\nMain Job: {}\nSupport Job: {}\nInnate Feral Skills: {}\nEquipped Feral Skills: {}\nDicipline Level: {}\nTemperament...".format(self.family, self.level, self.main_job, self.support_job, self.innate_feral_skills, self.equipped_feral_skills, self.discipline_level)
def get_soul_plate_description(self):
try:
return "*Family:*\n**{}**\nJob Trait: {}\nFeral Points: {}".format(self.family_name, Jobs().get_job_name(self.main_job), self.get_fp())
except AttributeError:
return "Old Format monster, unable to display."
#return "Family: {}\nLevel: {}\nMain Job: {}\nSupport Job: {}\nInnate Feral Skills: {}\nEquipped Feral Skills: {}\nDicipline Level: {}\nTemperament...".format(self.family, self.level, self.main_job, self.support_job, self.innate_feral_skills, self.equipped_feral_skills, self.discipline_level)
def get_reflector_description(self):
try:
return "**{}**\n*Stats:*\nFamily: {}\nLevel: {}\nMain Job: {}\nSupport Job: {}\nInnate Feral Skills: {}\nEquipped Feral Skills: {}\nFeral Points: {}\n\nWins: {}\nLosses: {}".format(self.monster_type, self.family_name, self.level, Jobs().get_job_name(self.main_job), Jobs().get_job_name(self.support_job), ', '.join(self.innate_feral_skills), ', '.join(self.equipped_feral_skills), self.get_fp(), self.wins, self.losses)
except AttributeError:
return "Old Format monster, unable to display."
#return "Family: {}\nLevel: {}\nMain Job: {}\nSupport Job: {}\nInnate Feral Skills: {}\nEquipped Feral Skills: {}\nDicipline Level: {}\nTemperament...".format(self.family, self.level, self.main_job, self.support_job, self.innate_feral_skills, self.equipped_feral_skills, self.discipline_level)
def get_hp(self):
......@@ -336,11 +380,25 @@ class Monster:
else:
return "Obedient"
def get_combat_skill(self):
# This is a very very complicated relationship betwen job and weapon type and how often it is used.
# We are going to generalize it since it's a pain in the ass and just take the average A+ rating and get the cap for the current level
return int(round(max(5, self.level * 2.88)))
def get_dexterity(self):
return int(round(max(self.base_dex, self.level * self.dex_per_level)))
def get_accuracy(self):
return int(math.floor(3*self.get_dexterity()/4) + 100)
# We ignore magic / ranged / melee here and give everyone a good acc. This could be improved by doing a lookup for each type and based on the
# type of monster (magic, range, or melee) then adjust the combat skill according to what type of attack is being performed.
return int(math.floor(3*self.get_dexterity()/4) + self.get_combat_skill())
def get_evasion(self):
job_eva = evasion_by_job[self.main_job]
base_eva = int(round(max(job_eva['base_eva'], self.level * job_eva['eva_per_level'])))
return base_eva + int(self.get_agility() / 2)
def get_vitality(self):
return int(round(max(self.base_vit, self.level * self.vit_per_level)))
......@@ -358,6 +416,14 @@ class Monster:
print(self.hp)
return max(0, int(round((self.hp/self.get_hp())*100)))
def get_hit_rate(self, enemy_eva, level_difference):
rate = 75 + math.floor(((self.get_accuracy() - enemy_eva)/2)) - (2*level_difference)
if rate < 20:
rate = 20
elif rate > 95:
rate = 95
return rate
# Calculate the base damage against the provided defense
# attack_type can be 'ranged' or 'melee'
def get_physical_base_damage(self, defense, attack_type, level_difference, enemy_vit):
......@@ -421,6 +487,9 @@ class Monster:
phy_damage = self.get_physical_base_damage(monster.get_base_defense(), 'melee', monster.level - self.level, monster.get_vitality())
return (phy_damage, 'hits')
def is_a_hit(self, monster):
hit_rate = self.get_hit_rate(monster.get_evasion(), monster.level - self.level)
return random.randint(1, 100) < hit_rate
def apply_damage(self, damage):
self.hp -= damage
......@@ -458,6 +527,12 @@ class AttackAction(Action):
self.damage = damage
self.message = message
class MissAction(Action):
def __init__(self, attacker, target, message):
self.attacker = attacker
self.target = target
self.message = message
class DefeatAction(Action):
def __init__(self, attacker, target, message, xp):
self.attacker = attacker
......@@ -465,6 +540,7 @@ class DefeatAction(Action):
self.message = message
self.xp = xp
class Arena:
def __init__(self, monster1, monster2, battle_type):
......@@ -489,26 +565,33 @@ class Arena:
primary = self.monster2
secondary = self.monster1
result1 = primary.attack(secondary)
secondary.apply_damage(result1[0])
actions.append(AttackAction(primary, secondary, result1[0], result1[1]))
if secondary.hp <= 0:
level_difference = secondary.level - primary.level
if level_difference in exp_table:
xp = exp_table[level_difference]
elif level_difference > 15:
xp = exp_table[15]
else:
xp = 0
actions.append(DefeatAction(primary, secondary, 'was defeated', xp))
return actions
result2 = secondary.attack(primary)
primary.apply_damage(result2[0])
actions.append(AttackAction(secondary, primary, result2[0], result2[1]))
if primary.hp <= 0:
xp = 200 + (20 * (primary.level - secondary.level))
actions.append(DefeatAction(secondary, primary, 'was defeated', xp))
return actions
if primary.is_a_hit(secondary):
result1 = primary.attack(secondary)
secondary.apply_damage(result1[0])
actions.append(AttackAction(primary, secondary, result1[0], result1[1]))
if secondary.hp <= 0:
level_difference = secondary.level - primary.level
if level_difference in exp_table:
xp = exp_table[level_difference]
elif level_difference > 15:
xp = exp_table[15]
else:
xp = 0
actions.append(DefeatAction(primary, secondary, 'was defeated', xp))
return actions
else:
actions.append(MissAction(primary, secondary, 'missed'))
if secondary.is_a_hit(primary):
result2 = secondary.attack(primary)
primary.apply_damage(result2[0])
actions.append(AttackAction(secondary, primary, result2[0], result2[1]))
if primary.hp <= 0:
xp = 200 + (20 * (primary.level - secondary.level))
actions.append(DefeatAction(secondary, primary, 'was defeated', xp))
return actions
else:
actions.append(MissAction(secondary, primary, 'missed'))
return actions
......@@ -552,7 +635,10 @@ if __name__ == "__main__":
print("Acc: {}".format(monster.get_accuracy()))
print("Eva: {}".format(monster.get_evasion()))
print("Acc: {}".format(monster2.get_accuracy()))
print("Eva: {}".format(monster2.get_evasion()))
battle_arena = p.start_battle(monster, monster2, "derp")
fighting = True
......@@ -560,6 +646,8 @@ if __name__ == "__main__":
actions = battle_arena.step()
time.sleep(2)
for action in actions:
if isinstance(action, MissAction):
print("{} {} {}.".format(action.attacker.monster_type, action.message, action.target.monster_type))
if isinstance(action, AttackAction):
print("{} {} {} for {}.".format(action.attacker.monster_type, action.message, action.target.monster_type, action.damage))
if isinstance(action, DefeatAction):
......