class SealOptionData(db.Model): __tablename__ = "seal_option_data" _mapper_utils = { "files": { "server": ["s_SealOptionValueData.bin"] }, } code = CustomColumn(db.String(32), primary_key=True, mapper_key="코드") effect_code = CustomColumn(db.Enum(EffectCode), nullable=False, mapper_key="효과코드", transform=lambda v: EffectCode(v)) operator = CustomColumn(db.String(4), mapper_key="Operator", transform=lambda v: v if v != "#" else None) def to_dict(self) -> dict: intervalls = {} for cname in COLUMN_NAMES: intervalls[cname.lower()] = getattr(self, cname) return { "code": self.code, "effect_code": self.effect_code.to_dict(), "operator": self.operator, "intervalls": intervalls, }
class QuestMission(db.Model): __tablename__ = "quest_mission" index = db.Column(db.Integer, primary_key=True, autoincrement=True) work_type = db.Column(db.Enum(QuestWorkType), nullable=False) work_value = db.Column(db.String(32)) quest_code = db.Column(db.String(32), db.ForeignKey("quest.code"), nullable=False) quest = db.relationship("Quest", foreign_keys=[quest_code]) map_code = db.Column(db.String(32), db.ForeignKey("map.code")) map = db.relationship("Map", foreign_keys=[map_code], uselist=False) x = db.Column(db.Float) y = db.Column(db.Float) count = db.Column(db.Integer, nullable=False) npc_code = db.Column(db.String(32), db.ForeignKey("npc.code")) npc = db.relationship("Npc", foreign_keys=[npc_code], uselist=False) item_code = db.Column(db.String(32), db.ForeignKey("item_list.code")) item = db.relationship("ItemList", foreign_keys=[item_code], uselist=False) monster_code = db.Column(db.String(32), db.ForeignKey("monster.code")) monster = db.relationship("Monster", foreign_keys=[monster_code], uselist=False) quest_item_code = db.Column(db.String(32), db.ForeignKey("quest_item.code")) quest_item = db.relationship("QuestItem", foreign_keys=[quest_item_code], uselist=False) def to_dict(self) -> dict: return { "work_type": self.work_type.to_dict(), "work_value": self.work_value, "map": (self.map.to_dict(minimal=True) if self.map else None), "pos": { "x": self.x, "y": self.y, }, "count": self.count, "npc": self.npc.to_dict(minimal=True) if self.npc else None, "item": self.item.to_dict() if self.item else None, "monster": (self.monster.to_dict(minimal=True) if self.monster else None), "quest_item": (self.quest_item.to_dict( minimal=True) if self.quest_item else None) }
class RankingPlayerHistory(db.Model): __tablename__ = "ranking_player_history" index = db.Column(db.Integer, primary_key=True, autoincrement=True) server = db.Column(db.Enum(Server), nullable=False) name = db.Column(db.String(16), nullable=False) previous_level_land = db.Column(db.Integer) new_level_land = db.Column(db.Integer) previous_level_sea = db.Column(db.Integer) new_level_sea = db.Column(db.Integer) previous_character_class = db.Column(db.Enum(CharacterClass)) new_character_class = db.Column(db.Enum(CharacterClass)) previous_guild = db.Column(db.String(16)) new_guild = db.Column(db.String(16)) inserted_at = db.Column(db.DateTime, default=get_utc_now) def to_dict(self) -> dict: changes = {} if self.previous_level_land: changes["previous_level_land"] = self.previous_level_land changes["new_level_land"] = self.new_level_land if self.previous_level_sea: changes["previous_level_sea"] = self.previous_level_sea changes["new_level_sea"] = self.new_level_sea if self.previous_character_class: changes["previous_character_class"] = ( self.previous_character_class.to_dict()) changes["new_character_class"] = self.new_character_class.to_dict() if self.previous_guild or self.new_guild: changes["previous_guild"] = self.previous_guild changes["new_guild"] = self.new_guild return { "inserted_at": str(self.inserted_at), "changes": changes, }
class SealOption(db.Model): __tablename__ = "seal_option" _mapper_utils = { "files": { "server": ["s_SealOptionData.bin"] }, } code = CustomColumn(db.String(32), primary_key=True, mapper_key="코드") # Link seal option to table. Used to reference the table later. # This is done in the update_database.py loop. seal_option_type = CustomColumn(db.Enum(SealOptionType), nullable=False, mapper_key="_seal_option_type")
class Accessory( db.Model, ExtraEquipmentMixin, ItemSetMixin, DroppedByMixin, ProducedByMixin, NeededForMixin, RandomBoxMixin, SoldByMixin, ): __tablename__ = "accessory" _mapper_utils = { "files": { "server": ["s_AccessoryItem.bin"], "client": ["c_AccessoryItemRes.bin"], "string": ["AccessoryItemStr.dat"], }, } accessory_type = CustomColumn(db.Enum(AccessoryType), nullable=False, mapper_key="구분코드", transform=lambda v: AccessoryType(v)) def to_dict(self, minimal: bool = False) -> dict: minimal_dict = { **ExtraEquipmentMixin.to_dict(self, minimal), "accessory_type": self.accessory_type.to_dict(), } if minimal: return minimal_dict return { **minimal_dict, **ItemSetMixin.to_dict(self), **DroppedByMixin.to_dict(self), **ProducedByMixin.to_dict(self), **NeededForMixin.to_dict(self), **RandomBoxMixin.to_dict(self), **SoldByMixin.to_dict(self), }
class Monster(db.Model): __tablename__ = "monster" _mapper_utils = { "files": { "server": ["s_MonsterChar.bin"], "client": ["c_MonsterCharRes.bin"], "string": ["MonsterCharStr.dat"], }, "options": { "image_key": "모델명" }, } index = db.Column(db.Integer, nullable=False) code = CustomColumn(db.String(32), primary_key=True, mapper_key="코드") name = CustomColumn(db.String(256), nullable=False, mapper_key="_name") icon = CustomColumn(db.String(32), nullable=False, mapper_key="_icon") rating_type = CustomColumn(db.Enum(RatingType), nullable=False, mapper_key="몬스터등급타입", transform=lambda val: RatingType(val)) level = CustomColumn(db.Integer, nullable=False, mapper_key="기준레벨") hp = CustomColumn(db.Integer, nullable=False, mapper_key="기준최대HP") range = CustomColumn(db.Enum(MonsterRange), nullable=False, mapper_key="공격거리타입", transform=lambda val: MonsterRange(val)) area = CustomColumn(db.Enum(Area), nullable=False, mapper_key="필드구분", transform=lambda val: Area(val)) experience = CustomColumn(db.Integer, nullable=False, mapper_key="보상경험치") minimal_damage = CustomColumn(db.Integer, nullable=False, mapper_key="최소물공력") maximal_damage = CustomColumn(db.Integer, nullable=False, mapper_key="최대물공력") physical_defense = CustomColumn(db.Integer, nullable=False, mapper_key="물방력") magic_defense = CustomColumn(db.Integer, nullable=False, mapper_key="마항력") attack_range = CustomColumn(db.Float, nullable=False, mapper_key="기본사정거리", transform=florensia_meter_transform) tameable = CustomColumn(db.Boolean, mapper_key="테이밍", nullable=False) drops = db.relationship( "Drop", primaryjoin="foreign(Drop.monster_code) == Monster.code") map_points = db.relationship( "MapPoint", primaryjoin="foreign(MapPoint.monster_code) == Monster.code") quest_missions = db.relationship( "QuestMission", primaryjoin="foreign(QuestMission.monster_code) == Monster.code") vision_range = CustomColumn(db.Float, nullable=False, mapper_key="선공시야", transform=florensia_meter_transform) # If you perform an action close to the range # attack vision range of the monster, you will # also get aggro attack_vision_range = CustomColumn(db.Float, nullable=False, mapper_key="요청시야", transform=florensia_meter_transform) messages_code = CustomColumn(db.String(32), mapper_key="오브젝트채팅", transform=lambda v: v if v != "#" else None) monster_message = db.relationship("MonsterMessage", uselist=False) # Skill 1 skill_1_code = CustomColumn(db.String(32), db.ForeignKey("monster_skill.code"), mapper_key="부가Action1코드", transform=lambda v: v if v != "#" else None) skill_1_chance = CustomColumn(db.Float, mapper_key="부가Action1선택율", transform=florensia_probability_transform) skill_1 = db.relationship("MonsterSkill", foreign_keys=[skill_1_code]) # Skill 2 skill_2_code = CustomColumn(db.String(32), db.ForeignKey("monster_skill.code"), mapper_key="부가Action2코드", transform=lambda v: v if v != "#" else None) skill_2_chance = CustomColumn(db.Float, mapper_key="부가Action2선택율", transform=florensia_probability_transform) skill_2 = db.relationship("MonsterSkill", foreign_keys=[skill_2_code]) def to_dict(self, minimal: bool = False) -> dict: minimal_dict = { "code": self.code, "name": self.name, "icon": self.icon, "rating": self.rating_type.to_dict(), "level": self.level, "area": self.area.to_dict(), } if minimal: return minimal_dict # Get monster skills as a list skills = [] for i in range(1, 3): skill = getattr(self, f"skill_{i}") if skill: skill_dict = skill.to_dict() skill_dict["chance"] = getattr(self, f"skill_{i}_chance") skills.append(skill_dict) return { **minimal_dict, "hp": self.hp, "range": self.range.to_dict(), "experience": self.experience, "minimal_damage": self.minimal_damage, "maximal_damage": self.maximal_damage, "physical_defense": self.physical_defense, "magic_defense": self.magic_defense, "attack_range": self.attack_range, "tamable": self.tameable, "vision_range": self.vision_range, "attack_vision_range": self.attack_vision_range, "messages": (self.monster_message.to_dict() if self.monster_message else None), "skills": skills, "map_points": [point.to_dict(map_dict=True) for point in self.map_points], "drops": [drop.to_dict(item_dict=True) for drop in self.drops], "quests": [ mission.quest.to_dict(minimal=True) for mission in self.quest_missions ] }
return { "items": [ item.to_dict(with_item_data=True) for item in item_columns if item ], "effects": effects, } # Add effect columns for i in range(1, 13): # Code setattr( ItemSet, f"effect_{i}_code", CustomColumn(db.Enum(EffectCode), mapper_key=f"효과코드_{i}", transform=(lambda v: EffectCode(v) if v != MAX_INT else None))) # Operator setattr( ItemSet, f"effect_{i}_operator", CustomColumn(db.String(4), mapper_key=f"수치연산자_{i}", transform=lambda v: v if v != "#" else None)) # Value setattr( ItemSet, f"effect_{i}_value", CustomColumn(db.Float, mapper_key=f"효과값_{i}",
class Essence( db.Model, BaseMixin, BonusMixin, DroppedByMixin, NeededForMixin, ProducedByMixin, RandomBoxMixin, ): __tablename__ = "essence" _mapper_utils = { "files": { "server": ["s_ArtifactItem.bin"], "client": ["c_ArtifactRes.bin"], "string": ["ArtifactStr.dat"], }, } equip_type = CustomColumn(db.Enum(EssenceEquipType), nullable=False, mapper_key="장착대상", transform=lambda v: EssenceEquipType(int(v))) required_weapon_level = CustomColumn(db.Integer, nullable=False, mapper_key="육상LV") is_core_essence = CustomColumn(db.Boolean, nullable=False, mapper_key="AtI타입") mounting_cost = CustomColumn(db.Integer, nullable=False, mapper_key="고정비용") mounting_item_level_cost = CustomColumn(db.Integer, nullable=False, mapper_key="LV비용") mounting_unit_cost = CustomColumn(db.Integer, nullable=False, mapper_key="비용단위") def to_dict(self, minimal: bool = False) -> dict: minimal_dict = { **BaseMixin.to_dict(self, minimal), **BonusMixin.to_dict(self), "equip_type": self.equip_type.to_dict(), "is_core_essence": self.is_core_essence, "required_weapon_level": self.required_weapon_level, } if minimal: return minimal_dict return { **minimal_dict, **DroppedByMixin.to_dict(self), **ProducedByMixin.to_dict(self), **NeededForMixin.to_dict(self), **RandomBoxMixin.to_dict(self), }
class StatusData(db.Model): """ Includes data for all status points (up to ~1000). point_type can be either the listed columns (max_hp, max_mp ...) or level. If it is level, the values are the base values. For all other types, just the increment is stored. (e.g. from 5 to 6 con, you get +30 max hp). level is either the real level for the character or the level of the status point (e.g. 300, if you have 300 points invested in con). """ __tablename__ = "status_data" index = db.Column(db.Integer, primary_key=True, autoincrement=True) point_type = db.Column(db.String(32), nullable=False) character_class = db.Column(db.Enum(CharacterClass), nullable=False) level = db.Column(db.Integer, nullable=False) max_hp = db.Column(db.Integer, nullable=False) max_mp = db.Column(db.Integer, nullable=False) avoidance = db.Column(db.Integer, nullable=False) melee_min_attack = db.Column(db.Integer, nullable=False) melee_max_attack = db.Column(db.Integer, nullable=False) melee_hitrate = db.Column(db.Integer, nullable=False) melee_critical_rate = db.Column(db.Integer, nullable=False) range_min_attack = db.Column(db.Integer, nullable=False) range_max_attack = db.Column(db.Integer, nullable=False) range_hitrate = db.Column(db.Integer, nullable=False) range_critical_rate = db.Column(db.Integer, nullable=False) magic_min_attack = db.Column(db.Integer, nullable=False) magic_max_attack = db.Column(db.Integer, nullable=False) magic_hitrate = db.Column(db.Integer, nullable=False) magic_critical_rate = db.Column(db.Integer, nullable=False) def to_dict(self) -> dict: # Dict only contains values that are non-zero fields = [ "max_hp", "max_mp", "avoidance", "melee_min_attack", "melee_max_attack", "melee_hitrate", "melee_critical_rate", "range_min_attack", "range_max_attack", "range_hitrate", "range_critical_rate", "magic_min_attack", "magic_max_attack", "magic_hitrate", "magic_critical_rate", ] dic = { "level": self.level, # "point_type": self.point_type, # "character_class": self.character_class.to_dict(), } for field in fields: value = getattr(self, field) if value != 0: dic[field] = value return dic
class Quest(db.Model): __tablename__ = "quest" index = db.Column(db.Integer, nullable=False) code = db.Column(db.String(32), primary_key=True, nullable=False) level = db.Column(db.Integer, nullable=False) area = db.Column(db.Enum(Area), nullable=False) class_ = db.Column(db.String(16)) money = db.Column(db.Integer) experience = db.Column(db.Integer) title = db.Column(db.String(256), nullable=False) selectable_items_count = db.Column(db.Integer) before_quest_code = db.Column(db.String(32)) before_quest = db.relationship( "Quest", primaryjoin="foreign(Quest.code) == Quest.before_quest_code", uselist=False) after_quest = db.relationship( "Quest", primaryjoin="foreign(Quest.before_quest_code) == Quest.code", uselist=False) start_npc_code = db.Column(db.String(32), db.ForeignKey("npc.code")) start_npc = db.relationship("Npc", foreign_keys=[start_npc_code], uselist=False) end_npc_code = db.Column(db.String(32), db.ForeignKey("npc.code")) end_npc = db.relationship("Npc", foreign_keys=[end_npc_code], uselist=False) start_area_code = db.Column(db.String(32), db.ForeignKey("map.code")) start_area = db.relationship("Map", foreign_keys=[start_area_code]) missions = db.relationship("QuestMission") selectable_items = db.relationship("QuestSelectableItem") give_items = db.relationship("QuestGiveItem") descriptions = db.relationship( "QuestDescription", primaryjoin="foreign(QuestDescription.quest_code) == Quest.code") def to_dict(self, minimal: bool = False) -> dict: minimal_dict = { "code": self.code, "area": self.area.to_dict(), "class": self.class_, "level": self.level, "title": self.title, } if minimal: return minimal_dict return { **minimal_dict, "money": self.money, "experience": self.experience, "selectable_items_count": self.selectable_items_count, "start_npc": (self.start_npc.to_dict(minimal=True) if self.start_npc else None), "end_npc": (self.end_npc.to_dict(minimal=True) if self.end_npc else None), "start_area": (self.start_area.to_dict( minimal=True) if self.start_area else None), "before_quest": (self.before_quest.to_dict( minimal=True) if self.before_quest else None), "after_quest": (self.after_quest.to_dict( minimal=True) if self.after_quest else None), "missions": [mission.to_dict() for mission in self.missions], "give_items": [gitem.to_dict() for gitem in self.give_items], "selectable_items": [sitem.to_dict() for sitem in self.selectable_items], "descriptions": {desc.language: desc.to_dict() for desc in self.descriptions}, }