Files
igncore/modules/standard/loot/loot_controller.py
T

413 lines
20 KiB
Python

import re
import secrets
import time
from collections import OrderedDict
from core.chat_blob import ChatBlob
from core.command_alias_service import CommandAliasService
from core.command_param_types import Const, Int, Any
from core.db import DB
from core.decorators import instance, command, timerevent
from core.igncore import IgnCore
from core.setting_service import SettingService
from core.text import Text
from modules.core.accounting.services.account_service import AccountService
from modules.raidbot.raid.raidbot_controller import Raider
from modules.standard.items.items_controller import ItemsController
from modules.standard.loot.item_types import LootItem
from modules.standard.raid.leader_controller import LeaderController
@instance()
class LootController:
NOT_LEADER_MSG = "Error! You must be raid leader, or have higher access " \
"level than the raid leader to use this command."
def __init__(self):
self.loot_list = OrderedDict()
self.last_modify = None
def inject(self, registry):
self.bot: IgnCore = registry.get_instance("bot")
self.db: DB = registry.get_instance("db")
self.text: Text = registry.get_instance("text")
self.leader_controller: LeaderController = registry.get_instance("leader_controller", is_optional=True)
self.setting_service: SettingService = registry.get_instance("setting_service")
self.items_controller: ItemsController = registry.get_instance("items_controller")
self.raid_controller = None
self.raid_controller = registry.get_instance("raidbot_controller", is_optional=True)
self.account_service: AccountService = registry.get_instance("account_service")
self.alts_service: AccountService = registry.get_instance("account_service")
self.alias: CommandAliasService = registry.get_instance("command_alias_service")
def pre_start(self):
self.db.load_sql_file(self.module_dir + "/raid_loot.sql", pre_optimized=True)
self.db.create_view("raid_loot")
self.alias.add_alias("add", "loot add")
self.alias.add_alias("rem", "loot rem")
self.alias.add_alias("remove", "loot remo")
self.alias.add_alias("flatroll", "loot roll")
self.alias.add_alias("result", "loot roll")
self.alias.add_alias("results", "loot roll")
self.alias.add_alias("list", "loot")
self.alias.add_alias("win", "loot roll")
@command(command="loot", params=[], description="Show the list of added items", access_level="member")
def loot_cmd(self, _):
if not self.loot_list:
return "Loot list is empty."
return self.get_loot_list()
@command(command="loot", params=[Const("edit")], description="Show the list of added items", access_level="leader",
sub_command="modify")
def loot_edit_cmd(self, _, _1):
if not self.loot_list:
return "Loot list is empty."
return self.get_loot_list(edit=True)
@command(command="loot", params=[Const("clear")], description="Clear all loot", access_level="leader",
sub_command="modify")
def loot_clear_cmd(self, request, _):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if self.loot_list:
self.loot_list.clear()
self.last_modify = None
self.bot.send_private_channel_message("Loot list cleared.")
else:
return "Loot list is already empty."
@command(command="loot", params=[Const("remitem"), Int("item_index")],
description="Remove an existing loot item", access_level="leader", sub_command="modify")
def loot_rem_item_cmd(self, request, _, item_index: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if not self.loot_list:
return "Loot list is empty."
try:
if self.loot_list[item_index]:
self.last_modify = int(time.time())
self.bot.send_private_channel_message(
f"Removed {self.loot_list.pop(item_index).get_item_str()} from loot list.")
else:
return "Item error."
except KeyError:
return "Wrong index given."
@command(command="loot", params=[Const("increase"), Int("item_index")], description="Increase item count",
access_level="leader", sub_command="modify")
def loot_increase_item_cmd(self, request, _, item_index: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if not self.loot_list:
return "Loot list is empty."
try:
loot_item = self.loot_list[item_index]
if loot_item:
loot_item.count += 1
self.last_modify = int(time.time())
self.bot.send_private_channel_message(
f"Increased item count for {loot_item.get_item_str()} to {loot_item.count:d}.")
else:
return "Item error."
except KeyError:
return "Wrong index given."
@command(command="loot", params=[Const("decrease"), Int("item_index")], description="Decrease item count",
access_level="leader", sub_command="modify")
def loot_decrease_item_cmd(self, request, _, item_index: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if not self.loot_list:
return "Loot list is empty."
try:
loot_item = self.loot_list[item_index]
if loot_item:
loot_item.count = loot_item.count - 1 if loot_item.count > 1 else 1
self.last_modify = int(time.time())
self.bot.send_private_channel_message(
f"Decreased item count for {loot_item.get_item_str()} to {loot_item.count:d}.")
else:
return "Item error."
except KeyError:
return "Wrong index given."
@command(command="loot", params=[Const("add"), Int("item_index")], description="Add yourself to item",
access_level="member")
def loot_add_to_cmd(self, request, _, item_index: int):
main = self.alts_service.get_account(request.sender.char_id)
if self.raid_controller:
raider: Raider = self.raid_controller.is_in_raid(main.get("char_id", -1))
if type(raider) == Raider:
if not raider.is_active:
self.bot.send_mass_message(request.sender.char_id,
"You are not participating in the raid, and cannot add to the loot.")
return
elif raider is None:
self.bot.send_mass_message(request.sender.char_id,
"You are not participating in the raid, and cannot add to the loot.")
return
try:
loot_item = self.loot_list[item_index]
old_item = self.is_already_added(request.sender.name)
if old_item:
if old_item.get_item_str() == loot_item.get_item_str():
name = "You have" if request.channel == "msg" else request.sender.name
self.bot.send_mass_message(request.sender.char_id,
f"{name} already added to {loot_item.get_item_str()}.")
old_item.bidders.remove(request.sender.name)
loot_item.bidders.append(request.sender.name)
self.last_modify = int(time.time())
if old_item is not None:
text = f"moved from {old_item.get_item_str()} to {loot_item.get_item_str()}."
self.bot.send_mass_message(request.sender.char_id, f"You have {text}")
self.bot.send_private_channel_message(request.sender.name + " " + text)
else:
text = f"added to {loot_item.get_item_str()}."
self.bot.send_mass_message(request.sender.char_id, f"You have {text}")
self.bot.send_private_channel_message(request.sender.name + " " + text)
except KeyError:
return "Wrong index given."
@command(command="loot", params=[Const("rem")], description="Remove yourself from item", access_level="member")
def loot_rem_from_cmd(self, request, _):
try:
loot_item = self.is_already_added(request.sender.name)
if loot_item is not None:
loot_item.bidders.remove(request.sender.name)
self.last_modify = int(time.time())
text = f"removed from {loot_item.get_item_str()}."
self.bot.send_private_message(request.sender.char_id, f"You were {text}")
self.bot.send_private_channel_message(request.sender.name + " was " + text)
else:
return "You are not added to any loot."
except KeyError:
return "Wrong index given."
@command(command="loot", params=[Const("roll")], description="Roll all loot", access_level="leader",
sub_command="modify")
def loot_roll_cmd(self, request, _):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if self.loot_list:
blob = ""
remove = []
for i, loot_item in self.loot_list.items():
winners = []
if loot_item.bidders:
if len(loot_item.bidders) <= loot_item.count:
winners = loot_item.bidders.copy()
loot_item.count -= len(loot_item.bidders)
loot_item.bidders = []
else:
for j in range(0, loot_item.count):
winner = secrets.choice(loot_item.bidders)
winners.append(winner)
loot_item.bidders.remove(winner)
loot_item.count = loot_item.count - 1 if loot_item.count > 0 else 0
for user in winners:
self.account_service.add_log(self.account_service.character_service.resolve_char_to_id(user),
"loot",
f"{loot_item.get_item_str()}",
request.sender.char_id)
blob += f"{i:d}. {loot_item.get_item_str()}\n"
blob += f" | Winners: <red>{'</red>, <red>'.join(winners)}</red>\n\n"
if loot_item.count == 0:
remove.append(i)
for i in remove:
self.loot_list.pop(i)
msg = ChatBlob("Roll results", blob, "\n________________\n\n<red>Time is UP!</red>\n", "\n________________")
self.bot.send_private_channel_message(msg if len(blob) > 0 else "No one was added to any loot")
if self.loot_list:
count = 1
for key in sorted(list(self.loot_list.keys())):
if self.loot_list[key].count <= 0:
del self.loot_list[key]
else:
loot_item = self.loot_list[key]
del self.loot_list[key]
self.loot_list[count] = loot_item
count += 1
if len(self.loot_list) > 0:
self.bot.send_private_channel_message(f"Theres still loot left to roll, the list has been rebuilt.")
else:
return "No loot to roll."
@command(command="loot", params=[Const("addraiditem"), Int("raid_item_id"), Int("item_count")],
description="Add item from pre-defined raid to loot list", access_level="leader", sub_command="modify")
def loot_add_raid_item(self, request, _, raid_item_id: int, item_count: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
sql = "SELECT r.name, r.comment, r.ql, a.lowid AS low_id, a.highid AS high_id FROM aodb a " \
"LEFT JOIN raid_loot r ON (a.name = r.name AND a.highql >= r.ql) " \
"WHERE r.id = ? LIMIT 1"
item = self.db.query_single(sql, [raid_item_id])
if item:
self.add_item_to_loot(item, item.comment, item_count)
self.bot.send_private_channel_message(f"Added {item.name} to loot list.")
else:
return f"Failed to add item with ID {raid_item_id}."
@command(command="loot", params=[Const("addraid"), Any("raid"), Any("category")],
description="Add all loot from pre-defined raid", access_level="leader", sub_command="modify")
def loot_add_raid_loot(self, request, _, raid: str, category: str):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
items = self.db.query(
"SELECT r.raid, r.category, r.id, r.ql, r.name, r.comment, r.multiloot, "
"a.lowid AS low_id, "
"a.highid AS high_id, a.icon "
"FROM raid_loot r "
"LEFT JOIN aodb a "
"ON (r.name = a.name AND r.ql <= a.highql) "
"WHERE r.raid = ? AND r.category = ? "
"ORDER BY r.name",
[raid, category]
)
if items:
for item in items:
self.add_item_to_loot(item, item.comment, item.multiloot)
self.bot.send_private_channel_message(f"{category} table was added to loot.")
else:
return f"{category} does not have any items registered in loot table."
@command(command="loot",
params=[Const("additem", is_optional=True), Int("item"), Int("item_count", is_optional=True)],
description="Add an item to loot list by item id", access_level="leader", sub_command="modify")
def loot_add_item_id_cmd(self, request, _, item_id, item_count: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if item_count is None:
item_count = 1
item = self.items_controller.get_by_item_id(item_id)
if not item:
return f"Could not find item with ID <highlight>{item_id}</highlight>."
self.add_item_to_loot(item, item_count=item_count)
self.bot.send_private_channel_message(f"{item.name} was added to loot list.")
@command(command="loot",
params=[Const("additem", is_optional=True), Any("item"), Int("item_count", is_optional=True)],
description="Add an item to loot list", access_level="leader", sub_command="modify")
def loot_add_item_cmd(self, request, _, item, item_count: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
loot = ""
if item_count is None:
item_count = 1
item = item.replace("&#39;", "'")
items = re.findall(r"(([^<]+)?<a href=[\"\']itemref://(\d+)/(\d+)/(\d+)[\"\']>([^<]+)</a>([^<]+)?)", item)
if items and item_count == 1:
for item in items:
item = self.text.make_item(int(item[2]), int(item[3]), int(item[4]), item[5])
if loot != "":
loot += ", " + item
else:
loot += item
self.add_item_to_loot(item)
else:
out = re.match(r"(([^<]+)?<a href=[\"\']itemref://(\d+)/(\d+)/(\d+)[\"\']>([^<]+)</a>([^<]+)?)", item)
if out:
# print(out.groups())
loot += item
item = self.text.make_item(int(out[3]), int(out[4]), int(out[5]), out[6])
self.add_item_to_loot((out[2] or "") + item + (out[7] or ""), item_count=item_count)
else:
loot += item
self.add_item_to_loot(item, item_count=item_count)
self.bot.send_private_channel_message(f"<highlight>{loot}</highlight> was added to loot list.")
@timerevent(budatime="1h",
description="Periodically check when loot list was last modified, and clear it "
"if last modification was done 1+ hours ago")
def loot_clear_event(self, _1, _2):
if self.loot_list and self.last_modify:
if int(time.time()) - self.last_modify > 3600 and self.loot_list:
self.last_modify = None
self.loot_list = OrderedDict()
self.bot.send_org_message("Loot was last modified more than 1 hour ago, list has been cleared.", fire_outgoing_event=False)
self.bot.send_private_channel_message(
"Loot was last modified more than 1 hour ago, list has been cleared.", fire_outgoing_event=False)
def is_already_added(self, name: str):
for i, loot_item in self.loot_list.items():
if name in loot_item.bidders:
return loot_item
return None
def add_item_to_loot(self, item, comment=None, item_count=1):
existing_item = next((loot_item for x, loot_item in self.loot_list.items() if loot_item.item == item), None)
if existing_item:
existing_item.count += 1
else:
# this prevents loot items from being re-numbered when items are removed
end_index = list(self.loot_list.keys())[-1] + 1 if len(self.loot_list) > 0 else 1
self.loot_list[end_index] = LootItem(item, comment, item_count)
self.last_modify = int(time.time())
def get_loot_list(self, edit=False):
blob = ""
remove = self.text.make_chatcmd("Remove from rolls", "/tell <myname> loot rem")
blob += f"[{remove}]\n\n"
for i, loot_item in self.loot_list.items():
bidders = loot_item.bidders
img = ""
if loot_item.get_item_str()[:2] == "<a" and loot_item.get_item_str()[-4:] == "</a>":
item = re.findall(r"(([^<]+)?<a href=[\"\']itemref://(\d+)/(\d+)/(\d+)[\"\']>([^<]+)</a>([^<]+)?)",
loot_item.get_item_str())
imgid = self.db.query_single("SELECT icon from aodb where lowid=? or highid=?",
[item[0][2], item[0][3]])
if imgid is not None:
img = self.text.make_image(imgid.get("icon")) + " - "
blob += f"{i:d}. {img}{item[0][0]} "
else:
blob += f"{i:d}. {loot_item.get_item_str()} "
blob += f"x{loot_item.count} [{self.text.make_chatcmd('Add', f'/tell <myname> loot add {i}')}]\n"
blob += ""
if edit:
blob += f"<yellow>[ {self.text.make_tellcmd('+1', f'loot increase {i}')} | " \
f"{self.text.make_tellcmd('-1', f'loot decrease {i}')} ]" \
f"[ {self.text.make_tellcmd('REM', f'loot remitem {i}')} ]</yellow>\n\n"
else:
if len(bidders) > 0:
blob += f"<yellow>{', '.join(bidders)}</yellow>\n\n"
else:
blob += "<yellow>None added</yellow>\n\n"
return ChatBlob(f"Loot ({len(self.loot_list):d})", blob)