import time from core.decorators import instance, event from core.igncore import IgnCore from core.logger import Logger from core.text import Text from modules.standard.helpbot.playfield_controller import PlayfieldController from modules.standard.tower.tower_events import TowerEventController @instance() class TowerAttackController: def __init__(self): self.logger = Logger(__name__) def inject(self, registry): self.bot: IgnCore = registry.get_instance("bot") self.db = registry.get_instance("db") self.text: Text = registry.get_instance("text") self.tower: TowerEventController = registry.get_instance("tower_controller") self.playfield_controller: PlayfieldController = registry.get_instance("playfield_controller") def start(self): self.db.shared.exec( "CREATE TABLE IF NOT EXISTS tower_attacker (" "id INT PRIMARY KEY AUTO_INCREMENT, " "att_org_name VARCHAR(50) NOT NULL, " "att_faction VARCHAR(10) NOT NULL, " "att_char_id INT, att_char_name VARCHAR(20) NOT NULL, " "att_level INT NOT NULL, " "att_ai_level INT NOT NULL, " "att_profession VARCHAR(15) NOT NULL, " "x_coord INT NOT NULL, " "y_coord INT NOT NULL, " "is_victory SMALLINT NOT NULL, " "tower_battle_id INT NOT NULL, " "created_at INT NOT NULL)") self.db.shared.exec( "CREATE TABLE IF NOT EXISTS tower_battle (" "id INT PRIMARY KEY AUTO_INCREMENT, " "playfield_id INT NOT NULL, " "site_number INT NOT NULL, " "def_org_name VARCHAR(50) NOT NULL, " "def_faction VARCHAR(10) NOT NULL, " "is_finished INT NOT NULL, " "battle_type VARCHAR(20) NOT NULL, " "last_updated INT NOT NULL)" "") self.db.create_view("tower_battle") self.db.create_view("tower_attacker") @event(event_type=TowerEventController.TOWER_ATTACK_EVENT, description="Create logentries for tower attacks", is_enabled=False) def tower_attack_event(self, _, event_data): t = int(time.time()) site_number = self.find_closest_site_number(event_data.location.playfield.id, event_data.location.x_coord, event_data.location.y_coord) attacker = event_data.attacker or {} defender = event_data.defender battle = self.find_or_create_battle(event_data.location.playfield.id, site_number, defender.org_name, defender.faction, "attack", t) self.db.exec( "INSERT INTO tower_attacker (att_org_name, att_faction, att_char_id, att_char_name, " "att_level, att_ai_level, att_profession, " "x_coord, y_coord, is_victory, tower_battle_id, created_at) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [attacker.get("org_name", ""), attacker.get("faction", ""), attacker.get("char_id", 0), attacker.get("name", ""), attacker.get("level", 0), attacker.get("ai_level", 0), attacker.get("profession", ""), event_data.location.x_coord, event_data.location.y_coord, 0, battle.id, t]) @event(event_type=TowerEventController.TOWER_VICTORY_EVENT, description="Record tower victories", is_enabled=False) def tower_victory_event(self, _, event_data): t = int(time.time()) if event_data.type == "attack": row = self.get_last_attack(event_data.winner.faction, event_data.winner.org_name, event_data.loser.faction, event_data.loser.org_name, event_data.location.playfield.id, t, is_finished=0) if not row: site_number = 0 is_finished = 1 self.db.exec( "INSERT INTO tower_battle (playfield_id, site_number, def_org_name, def_faction, " "is_finished, battle_type, last_updated) " "VALUES (?, ?, ?, ?, ?, ?, ?)", [event_data.location.playfield.id, site_number, event_data.loser.org_name, event_data.loser.faction, is_finished, event_data.type, t]) battle_id = self.db.last_insert_id() attacker = event_data.winner or {} self.db.exec( "INSERT INTO tower_attacker (att_org_name, att_faction, att_char_id, " "att_char_name, att_level, att_ai_level, att_profession, " "x_coord, y_coord, is_victory, tower_battle_id, created_at) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [attacker.get("org_name", ""), attacker.get("faction", ""), attacker.get("char_id", 0), attacker.get("name", ""), attacker.get("level", 0), attacker.get("ai_level", 0), attacker.get("profession", ""), 0, 0, 0, battle_id, t]) else: is_victory = 1 self.db.exec("UPDATE tower_attacker SET is_victory = ? WHERE id = ?", [is_victory, row.attack_id]) is_finished = 1 self.db.exec("UPDATE tower_battle SET is_finished = ?, last_updated = ? WHERE id = ?", [is_finished, t, row.battle_id]) elif event_data.type == "terminated": site_number = 0 is_finished = 1 row = self.find_similar_attacks(event_data.loser.faction, event_data.loser.org_name, event_data.location.playfield.id, t, finished=0) if row: self.db.exec("UPDATE tower_battle SET is_finished = ?, last_updated = ? WHERE id = ?", [is_finished, t, row.battle_id]) else: self.db.exec( "INSERT INTO tower_battle (playfield_id, site_number, def_org_name, def_faction, " "is_finished, battle_type, last_updated) VALUES (?, ?, ?, ?, ?, ?, ?)", [event_data.location.playfield.id, site_number, event_data.loser.org_name, event_data.loser.faction, is_finished, event_data.type, t]) else: raise Exception("Unknown victory event type: '%s'" % event_data.type) def find_closest_site_number(self, playfield_id, x_coord, y_coord): # noinspection SqlUnused sql = """ SELECT site_number, ((x_distance * x_distance) + (y_distance * y_distance)) radius FROM (SELECT playfield_id, site_number, min_ql, max_ql, x_coord, y_coord, site_name, (x_coord - ?) as x_distance, (y_coord - ?) as y_distance FROM tower_sites WHERE playfield_id = ?) t ORDER BY radius LIMIT 1""" row = self.db.query_single(sql, [x_coord, y_coord, playfield_id]) if row: return row.site_number else: return 0 def find_or_create_battle(self, playfield_id, site_number, org_name, faction, battle_type, t): last_updated = t - (8 * 3600) is_finished = 0 sql = """ SELECT * FROM tower_battle WHERE playfield_id = ? AND site_number = ? AND is_finished = ? AND def_org_name = ? AND def_faction = ? AND last_updated >= ? """ battle = self.db.query_single(sql, [playfield_id, site_number, is_finished, org_name, faction, last_updated]) if battle: self.db.exec("UPDATE tower_battle SET last_updated = ? WHERE id = ?", [t, battle.id]) return battle else: self.db.exec( "INSERT INTO tower_battle (playfield_id, site_number, def_org_name, def_faction, " "is_finished, battle_type, last_updated) VALUES (?, ?, ?, ?, ?, ?, ?)", [playfield_id, site_number, org_name, faction, is_finished, battle_type, t]) return self.db.query_single(sql, [playfield_id, site_number, is_finished, org_name, faction, last_updated]) def get_last_attack(self, att_faction, att_org_name, def_faction, def_org_name, playfield_id, t, is_finished=None): last_updated = t - (8 * 3600) sql = f""" SELECT b.id AS battle_id, a.id AS attack_id, b.playfield_id as pf_id, b.site_number as site FROM tower_battle b JOIN tower_attacker a ON a.tower_battle_id = b.id WHERE a.att_faction = ? AND a.att_org_name = ? AND b.def_faction = ? AND b.def_org_name = ? AND b.playfield_id = ? {'AND b.is_finished = ?' if is_finished is not None else ''} AND b.last_updated >= ? ORDER BY last_updated DESC LIMIT 1""" return self.db.query_single(sql, [att_faction, att_org_name, def_faction, def_org_name, playfield_id, is_finished, last_updated] if is_finished is not None else [att_faction, att_org_name, def_faction, def_org_name, playfield_id, last_updated]) def find_similar_attacks(self, def_faction, def_org_name, playfield_id, t, finished=None): last_updated = t - (8 * 3600) sql = f""" SELECT b.id AS battle_id, a.id AS attack_id, b.playfield_id as pf_id, b.site_number as site FROM tower_battle b JOIN tower_attacker a ON a.tower_battle_id = b.id WHERE b.def_faction = ? AND b.def_org_name = ? AND b.playfield_id = ? {'AND b.is_finished = ?' if finished else ''} AND b.last_updated >= ? ORDER BY last_updated DESC """ return self.db.query_single(sql, [def_faction, def_org_name, playfield_id, finished, last_updated] if finished else [def_faction, def_org_name, playfield_id, last_updated])