import sqlite3 import config from collections import OrderedDict db_name = config.db_file db_version = 1 ROLE_PLAYER = 10 ROLE_MASTER = 20 game_templates = OrderedDict([('fae', 'Fate Accelerated RPG'), ('fae-blank', 'Fate Accelerated RPG (empty character sheet)'), ('dnd', 'Dungeons & Dragons'), ('dnd-blank', 'Dungeons & Dragons (empty character sheet)')]) room_container = 'room' rolls_container = 'rolls' preferred_container_order = ['general', 'aspects', 'stunts', 'skills', 'spells', 'inventory', 'rolls'] preferred_key_order = {'general': ['description', 'fatepoints', 'stress2', 'stress4', 'stress6'], 'aspects': ['highconcept', 'trouble']} def open_connection(): return sqlite3.connect(db_name) def close_connection(db): db.close() def table_exists(db, table): c = db.cursor() query = c.execute('''SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?''', (table,)) result = query.fetchone() if result[0] == 0: return False else: return True class Group(): def __init__(self, gameid, groupid, groupname): self.gameid = gameid self.groupid = groupid self.groupname = groupname class Player(): def __init__(self, gameid, playerid, role, playername): self.gameid = gameid self.playerid = playerid self.role = role self.playername = playername def init(): db = open_connection() try: print('Initializing database...') c = db.cursor() if not table_exists(db, 'Games'): c.execute('''CREATE TABLE IF NOT EXISTS Games (gameid integer primary key autoincrement, version int, lastactivity datetime, gamename text, template text)''') if not table_exists(db, 'Groups'): c.execute('''CREATE TABLE IF NOT EXISTS Groups (gameid integer, groupid integer primary key, groupname text)''') if not table_exists(db, 'Players'): c.execute('''CREATE TABLE IF NOT EXISTS Players (gameid integer, playerid integer, role integer, playername text, PRIMARY KEY(gameid, playerid))''') if not table_exists(db, 'Contents'): c.execute('''CREATE TABLE IF NOT EXISTS Contents (gameid integer, playerid integer, container text, key text, value text, PRIMARY KEY(gameid, playerid, container, key))''') except: print('failed to initialize database') raise db.commit() close_connection(db) def convert_template(template): """ The blank versions of the character sheets use the same templates (same dices) but treat treat the sheet creation differently (...by skipping it) """ if template == 'fae-blank': return 'fae', True if template == 'dnd-blank': return 'dnd', True return template, False def new_game(db, admin, playername, gamename, groupid, groupname, template): """ Creates a new game. DOES NOT CHECK that the group is not already in a game. Returns: None - the group is already in a game. the gameid otherwise. """ if template not in game_templates: raise c = db.cursor() try: query = c.execute('''INSERT INTO Games(version, lastactivity, gamename, template) VALUES (?, datetime('now'), ?, ?)''', (db_version, gamename, template)) except sqlite3.IntegrityError: # probably group is already in game return None gameid = c.lastrowid try: query = c.execute('''INSERT INTO Groups(gameid, groupid, groupname) VALUES (?, ?, ?)''', (gameid, groupid, groupname,)) except sqlite3.IntegrityError: return None add_player(db, admin, playername, gameid, ROLE_MASTER) db.commit() return gameid def del_game(db, gameid): c = db.cursor() query = c.execute('''DELETE FROM Games WHERE gameid=?''', (gameid,)) query = c.execute('''DELETE FROM Groups WHERE gameid=?''', (gameid,)) query = c.execute('''DELETE FROM Players WHERE gameid=?''', (gameid,)) query = c.execute('''DELETE FROM Contents WHERE gameid=?''', (gameid,)) db.commit() def get_template_from_gameid(db, gameid): c = db.cursor() query = c.execute('''SELECT template FROM Games WHERE gameid=?''', (gameid,)) result = query.fetchone() if result == None: # invalid gameid or database not updated raise template = result[0] return template def get_template_from_groupid(db, groupid): c = db.cursor() query = c.execute('''SELECT template FROM Games LEFT JOIN Groups ON Games.gameid = Groups.gameid WHERE groupid=?''', (groupid,)) result = query.fetchone() if result == None: # group not in a game return None template = result[0] return template def get_group_from_playerid(db, playerid): c = db.cursor() query = c.execute('''SELECT Groups.gameid, groupid, groupname, playerid, role, playername FROM Groups LEFT JOIN Players ON Groups.gameid = Players.gameid WHERE playerid=?''', (playerid,)) result = query.fetchone() if result is None: return None, None group = Group(result[0], result[1], result[2]) player = Player(result[0], result[3], result[4], result[5]) return group, player def get_group_from_groupid(db, groupid): c = db.cursor() query = c.execute('''SELECT gameid, groupid, groupname FROM Groups WHERE groupid=?''', (groupid,)) result = query.fetchone() if result is None: return None group = Group(result[0], result[1], result[2]) return group def get_player_role(db, userid, gameid): c = db.cursor() query = c.execute('''SELECT role FROM Players WHERE playerid=? AND gameid=?''', (userid, gameid,)) result = query.fetchone() if result is None: return None role = result[0] return role def get_player_name(db, gameid, userid): c = db.cursor() query = c.execute('''SELECT playername FROM Players WHERE playerid=? AND gameid=?''', (userid, gameid,)) result = query.fetchone() if result is None: return None name = result[0] return name def add_player(db, userid, username, gameid, role): old_role = get_player_role(db, userid, gameid) new_player_added = False if old_role is None: new_player_added = True else: if old_role >= role: role = old_role c = db.cursor() query = c.execute('''INSERT OR REPLACE INTO Players(gameid, playerid, role, playername) VALUES (?, ?, ?, ?)''', (gameid, userid, role, username,)) db.commit() return new_player_added def get_masters_for_game(db, gameid): c = db.cursor() query = c.execute('''SELECT playerid FROM Players WHERE role=? AND gameid=?''', (ROLE_MASTER, gameid, )) ret = [] for player in query: ret.append(player[0]) return ret def get_all_players_from_game(db, gameid): c = db.cursor() query = c.execute('''SELECT gameid, playerid, role, playername FROM Players WHERE gameid=?''', (gameid, )) ret = [] for player in query: p = Player(player[0], player[1], player[2], player[3]) ret.append(p) return ret def add_default_items(db, userid, gameid, template): if template == 'fae': update_item(db, gameid, userid, 'general', 'description', 'Describe your character in a few words.', False) update_item(db, gameid, userid, 'general', 'fatepoints', '3', False) update_item(db, gameid, userid, 'general', 'refresh', '3', False) update_item(db, gameid, userid, 'general', 'stress2', 'Inactive', False) update_item(db, gameid, userid, 'general', 'stress4', 'Inactive', False) update_item(db, gameid, userid, 'general', 'stress6', 'Inactive', False) update_item(db, gameid, userid, 'stunts', '1', 'Set this to your first stunt.', False) update_item(db, gameid, userid, 'aspects', 'highconcept', 'Set this to your high concept.', False) update_item(db, gameid, userid, 'aspects', 'trouble', 'Your character\'s trouble.', False) update_item(db, gameid, userid, 'aspects', '1', 'Set this to your first aspect.', False) update_item(db, gameid, userid, 'approaches', 'careful', '0', False) update_item(db, gameid, userid, 'approaches', 'clever', '0', False) update_item(db, gameid, userid, 'approaches', 'flashy', '0', False) update_item(db, gameid, userid, 'approaches', 'forceful', '0', False) update_item(db, gameid, userid, 'approaches', 'quick', '0', False) update_item(db, gameid, userid, 'approaches', 'sneaky', '0', False) elif template == 'dnd': update_item(db, gameid, userid, 'general', 'class', 'Your class.', False) update_item(db, gameid, userid, 'general', 'race', 'Your race.', False) update_item(db, gameid, userid, 'general', 'alignment', 'Your alignment.', False) update_item(db, gameid, userid, 'general', 'level', '1', False) update_item(db, gameid, userid, 'attributes', 'strength', '14', False) update_item(db, gameid, userid, 'attributes', 'dexterity', '14', False) update_item(db, gameid, userid, 'attributes', 'constitution', '14', False) update_item(db, gameid, userid, 'attributes', 'intelligence', '14', False) update_item(db, gameid, userid, 'attributes', 'wisdom', '14', False) update_item(db, gameid, userid, 'attributes', 'charisma', '14', False) def number_of_games(db, user): c = db.cursor() query = c.execute('''SELECT count(*) FROM Players WHERE playerid=?''', (user,)) result = query.fetchone() return result[0] def get_game_from_group(db, groupid): c = db.cursor() query = c.execute('''SELECT gameid FROM Groups WHERE groupid=?''', (groupid,)) result = query.fetchone() if result is None: return None return result[0] def get_game_info(db, gameid): c = db.cursor() query = c.execute('''SELECT gamename, template FROM Games WHERE gameid=?''', (gameid,)) result = query.fetchone() gamename = result[0] template = result[1] query = c.execute('''SELECT groupname FROM Groups WHERE gameid=?''', (gameid,)) groups = [] for group in query: groups.append(group[0]) query = c.execute('''SELECT playername, role FROM Players WHERE gameid=?''', (gameid,)) players = {} for player in query: players[player[0]] = player[1] return gamename, template, groups, players def number_of_items(db, gameid, playerid): c = db.cursor() query = c.execute('''SELECT count(*) FROM Contents WHERE gameid=? AND playerid=?''', (gameid, playerid,)) result = query.fetchone() return result[0] def update_item(db, gameid, playerid, container, key, change, replace_only): """ change: can be a string, a number, or a relative change (e.g. '+1', '-1') replace_only: will avoid adding a new key Returns: oldvalue: None if didn't exist, int if it was digits, or a string newvalue: int if oldvalue was digits and change is a relative change, text otherwise """ c = db.cursor() query = c.execute('''SELECT value FROM Contents WHERE gameid=? AND playerid=? AND container=? AND key=?''', (gameid, playerid, container, key,)) result = query.fetchone() if result is None: oldvalue = None if replace_only: return oldvalue, None else: oldvalue = result[0] if (oldvalue is None or oldvalue.isdigit()) and (change.isdigit() or (change[0] in ['+', '-'] and change[1:].isdigit())): if oldvalue is None: oldvalue = 0 else: oldvalue = int(oldvalue) if change[0] == '+': newvalue = oldvalue + int(change[1:]) elif change[0] == '-': newvalue = oldvalue - int(change[1:]) else: newvalue = int(change) else: newvalue = change query = c.execute('''INSERT OR REPLACE INTO Contents(gameid, playerid, container, key, value) VALUES (?, ?, ?, ?, ?)''', (gameid, playerid, container, key, newvalue,)) db.commit() return oldvalue, newvalue def to_number(thing): """ Converts something to an integer. Returns the integer upon success, or 0 upon failure. """ try: test = int(thing) return test except: return 0 def add_to_list(db, gameid, playerid, container, description): """ description: the new item description """ c = db.cursor() query = c.execute('''SELECT key FROM Contents WHERE gameid=? AND playerid=? AND container=?''', (gameid, playerid, container,)) maximum = 0 for keys in query: maximum = max(to_number(keys[0]), maximum) new = maximum + 1 query = c.execute('''INSERT OR REPLACE INTO Contents(gameid, playerid, container, key, value) VALUES (?, ?, ?, ?, ?)''', (gameid, playerid, container, new, description,)) db.commit() def get_item_value(db, gameid, playerid, container, key): c = db.cursor() query = c.execute('''SELECT value FROM Contents WHERE gameid=? AND playerid=? AND container=? AND key=?''', (gameid, playerid, container, key,)) result = query.fetchone() if result == None: return None return result[0] def delete_item(db, gameid, playerid, container, key): c = db.cursor() query = c.execute('''SELECT value FROM Contents WHERE gameid=? AND playerid=? AND container=? AND key=?''', (gameid, playerid, container, key,)) result = query.fetchone() if result == None: return None oldvalue = result[0] query = c.execute('''DELETE FROM Contents WHERE gameid=? AND playerid=? AND container=? AND key=?''', (gameid, playerid, container, key,)) db.commit() return oldvalue def get_items(db, gameid, playerid): ret = {} c = db.cursor() contents = {} query = c.execute('''SELECT container, key, value FROM Contents WHERE gameid=? AND playerid=?''', (gameid, playerid,)) for row in query.fetchall(): if row[0] not in contents: contents[row[0]] = {} contents[row[0]][row[1]] = row[2] return contents