示例#1
0
class Main(NativeHacktool):
    CLASS_NAME = WINDOW_NAME = 'RESIDENT EVIL 6'

    def __init__(self):
        super().__init__()
        self.handler = MemHandler()
        self._global = models.Global(0, self.handler)
        self.ingame_item = models.IngameItem(0, self.handler)
        self.char_index = self._global.char_index = 0

    def render_main(self):
        person = (self._person, models.Character)

        with Group("player", "全局", self._global):
            ModelInput("skill_points", instance=(self._skill_points, models.SkillPoints))

        with Group("player", "角色", person):
            self.char_choice = Choice("角色", datasets.PERSONS, self.weak.on_person_change)
            ModelInput("health")
            ModelInput("health_max")
            ModelInput("stamina")
            ModelInput("stamina_max")
            ModelInput("moving_speed")
            ModelInput("rapid_fire")
            ModelInput("is_wet")
            ModelCoordWidget("coord", labels=('X坐标', 'Z坐标', 'Y坐标'), savable=True)
            ModelCheckBox("invincible")

        self.lazy_group(Group("person_items", "角色物品", person, serializable=False, cols=4), self.render_person_items)
        self.lazy_group(Group("person_skills", "角色技能", self._global, cols=4), self.render_person_skills)
        self.lazy_group(StaticGroup("代码插入"), self.render_assembly_functions)
        self.lazy_group(StaticGroup("功能"), self.render_functions)

    def render_person_items(self):
        """游戏中物品"""
        for label, index_range in (('水平武器', (0, 7)), ('药丸', (7, 8)), ('垂直武器', (8, 13)), ('其他物品', (15, 24))):
            r = 1
            for i in range(*index_range):
                prop = "items.%d" % i
                select = ModelChoiceDisplay(prop + ".type", "%s%d" % (label, r),
                    choices=datasets.INVENTORY_ITEMS.choices, values=datasets.INVENTORY_ITEMS.values)
                with select.container:
                    ui.Button("详情", className="btn_sm", onclick=partial(__class__.show_ingame_item, self.weak,
                        instance=self._person, prop=prop))
                r += 1

    def render_person_skills(self):
        with ModelSelect.choices_cache:
            for i in range(3):
                ModelSelect("char_skills.$char_index.%d" % i, "技能%d" % (i + 1), choices=datasets.SKILLS)

    def render_assembly_functions(self):
        # NOP_7 = b'\x90' * 7
        NOP_8 = b'\x90' * 8
        # NOP_9 = b'\x90' * 9
        super().render_assembly_functions((
            AssemblyItem('ammo_keep', '子弹不减', b'\x66\x29\x54\x41\x0A\x79\x07', 0x900000, 0xA00000,
                b'\x66\x4A\x90\x90\x90'),
            AssemblyItem('no_recoil', '无后坐力', b'\xF3\x0F\x10\x8E\xFC\x4A\x00\x00', 0x680000, 0x700000, NOP_8),
            AssemblyItem('rapid_fire', '快速射击', b'\xF3\x0F\x5C\xC2\xF3\x0F\x11\x86\x4C\x4F\x00\x00', 0x680000, 0x700000,
                b'', b'\xF3\x0F\x58\xD2\xF3\x0F\x58\xD2\xF3\x0F\x5C\xC2\xF3\x0F\x11\x86\x4C\x4F\x00\x00',
                inserted=True),
            AssemblyItem('merce_timer_keep', '佣兵模式时间不减',
                b'\xF3\x0F\x11\x86\x6C\x48\x00\x00\xF3\x0F\x11\x8E\x74\x48\x00\x00', 0x100000, 0x200000, NOP_8),
            AssemblyItem('god_on_hit_kill', '血不减+一击必杀', b'\x66\x8b\x44\x24\x04\x66\x29\x81\x10\x0f\x00\x00',
                0x600000, 0x700000, b'',
                b'\x83\x79\x38\x01\x75\x0A\xC7\x81\x10\x0F\x00\x00\x00\x00\x00\x00', inserted=True),
            AssemblyItem('skill_points', '技能点数', b'\x8B\xBE\x88\x05\x00\x00\x8B\x8E\x8C\x05\x00\x00',
                0x580000, 0x640000, b'', b'\x8B\xBE\x88\x05\x00\x00\x8B\x8E\x8C\x05\x00\x00\x89\x35%s',
                inserted=True, args=('skill_points_base',)),
        ))

    def render_functions(self):
        super().render_functions(('unlock_guns', 'give_rocket_launcher'))

    def get_ingame_item_dialog(self):
        """物品信息对话框"""
        name = 'ingame_item_dialog'
        dialog = getattr(self, name, None)
        if dialog is None:
            with DialogGroup(name, "物品详情", self.ingame_item, cols=1, dialog_style={'width': 800, 'height': 1400},
                    closable=False, horizontal=False, button=False) as dialog:
                ModelCheckBox("enabled")
                ModelInput("slot")
                ModelSelect("type", choices=datasets.INVENTORY_ITEMS.choices, values=datasets.INVENTORY_ITEMS.values,
                    instance=self.ingame_item)
                ModelInput("quantity")
                ModelInput("max_quantity")
                ModelInput("model", hex=True)
            with dialog.footer:
                ui.Button("复制", className='btn_sm', onclick=self.weak.ingame_item_copy)
                ui.Button("粘贴", className='btn_sm', onclick=self.weak.ingame_item_paste)

            setattr(self, name, dialog)
        return dialog

    def get_hotkeys(self):
        this = self.weak
        return (
            (VK.MOD_ALT, VK.H, this.pull_through),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.H, this.pull_through_all),
            (VK.MOD_ALT, VK.E, this.set_ammo_one),
            (VK.MOD_ALT, VK.R, this.set_ammo_full),
            (VK.MOD_ALT, VK.getCode(','), this.save_coord),
            (VK.MOD_ALT, VK.getCode('.'), this.load_coord),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.getCode(','), this.undo_coord),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.getCode('.'), this.reload_coord),
            (VK.MOD_ALT, VK.O, this.p1_go_p2),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.O, this.p2_go_p1),
        )

    def onattach(self):
        super().onattach()
        self._global.addr = self.handler.base_addr

    def _person(self):
        if self.handler.active:
            chars = self._global.character_struct.chars
            person = chars[self.char_index]
            if person.addr is 0:
                for i in range(len(datasets.PERSONS)):
                    if chars[i].addr:
                        self.char_choice.index = self.char_index = i
                        person = chars[i]
                        break
            return person

    def _person_config(self):
        if self.handler.active:
            return self._global.character_config.chars[self.char_index]

    def _skill_points(self):
        skill_points_base = self.get_variable('skill_points_base')
        if skill_points_base:
            return models.SkillPoints(self.handler.read32(skill_points_base.addr), self.handler)
        print('未初始化')

    @property
    def saved_items(self):
        return self._global.character_config.saved_items[self.char_index].items

    person = property(_person)
    person_config = property(_person_config)

    def on_person_change(self, lb):
        self.char_index = self._global.char_index = lb.index

    def show_ingame_item(self, view, instance, prop):
        """显示物品详情对话框"""
        if callable(instance):
            instance = instance()
        item = getattr(instance, prop)
        if item and item.addr:
            self.ingame_item.addr = item.addr
            dialog = self.get_ingame_item_dialog()
            dialog.read()
            dialog.show()
        else:
            print("没有数据")

    @property
    def saved_item_manager(self):
        addr = self.handler.remote_call(0x4F7230, 0)
        addr = self.handler.read32(addr + 0x658)
        return models.SavedItemManager(addr, self.handler)

    def set_ingame_item(self, slot, type, ammo, character=0):
        """设置物品 TODO: 加载物品模型"""
        func_addr = self.get_cached_address('_set_ingame_item', b'\x51\x53\x55\x8B\x6C\x24\x14\xC1', 0x600000, 0x700000)
        self.native_call_auto(func_addr, '5L', slot, type, ammo, 0, 0, this=character or self.person.addr)

    def set_ingame_saved_item(self, slot, type, quantity=0):
        """检查点间有效"""
        targets = []
        temp = self.saved_item_manager
        targets.append(temp.saved_items[self.char_index].items[slot])
        targets.append(temp.saved_items2[self.char_index].items[slot])
        targets.append(self._global.character_config.saved_item_manager.saved_items2[self.char_index].items[slot])
        for item in targets:
            item.type = type
            if quantity:
                item.quantity = quantity

    def ingame_item_copy(self, _):
        fefactory_api.set_clipboard(self.ingame_item.hex())

    def ingame_item_paste(self, _):
        self.ingame_item.fromhex(fefactory_api.get_clipboard())

    def pull_through(self):
        self.person.set_with('health', 'health_max').set_with('stamina', 'stamina_max')

    def pull_through_all(self):
        character_struct = self._global.character_struct
        for i in range(character_struct.chars_count):
            character_struct.chars[i].set_with('health', 'health_max')

    def set_ammo_full(self):
        person = self.person
        person.items[person.cur_item].set_with('quantity', 'max_quantity')

    def set_ammo_one(self):
        person = self.person
        person.items[person.cur_item].quantity = 1

    def save_coord(self):
        self.last_coord = self.person.coord.values()

    def load_coord(self):
        if hasattr(self, 'last_coord'):
            person = self.person
            self.prev_coord = person.coord.values()
            person.coord = self.last_coord

    def undo_coord(self):
        if hasattr(self, 'prev_coord'):
            self.person.coord = self.prev_coord

    def reload_coord(self):
        if hasattr(self, 'last_coord'):
            self.person.coord = self.last_coord

    def p1_go_p2(self):
        self._person()  # 确保当前角色正确
        chars = self._global.character_struct.chars
        chars[self.char_index].coord = chars[self.char_index + 1].coord.values()

    def p2_go_p1(self):
        self._person()  # 确保当前角色正确
        chars = self._global.character_struct.chars
        chars[self.char_index + 1].coord = chars[self.char_index].coord.values()

    def unlock_guns(self, _):
        """解锁横向武器"""
        items = self._global.character_config.saved_item_manager.saved_items2[self.char_index].items
        person = self.person
        for i in range(7):
            if items[i].type:
                person.items[i].enabled = True

    def give_rocket_launcher(self, _):
        """火箭发射器(检查点)"""
        self.set_ingame_saved_item(12, 0x11b, 1)
示例#2
0
class BaseGTATool(NativeHacktool):
    RAISE_UP_SPEED = 1.0
    GO_DOWN_SPEED = 0.5
    TO_UP_DELTA = 10
    TO_DOWN_DELTA = 6
    key_hook = False

    def __init__(self):
        super().__init__()
        self.handler = MemHandler()

    def get_hotkeys(self):
        """重写这个函数,返回要注册的热键列表"""
        return self.get_common_hotkeys()

    def inputCheat(self, text):
        auto.sendKey(TextVK(text), 10)

    def _player(self):
        player_addr = self.handler.read32(self.address.PLAYER_PTR)
        if player_addr is 0:
            return None
        player = getattr(self, '_player_ins', None)
        if not player:
            player = self._player_ins = self.Player(player_addr, self)
        elif player.addr != player_addr:
            player.addr = player_addr
        return player

    def _vehicle(self):
        addr = self.handler.read32(self.address.VEHICLE_PTR)
        if addr >= self.vehicle_pool.start:
            return self.Vehicle(self.handler.read32(self.address.VEHICLE_PTR), self) or None

    player = property(_player)
    vehicle = property(_vehicle)

    @property
    def last_vehicle(self):
        return self.player.vehicle or self.vehicle

    @property
    def isInVehicle(self):
        """当前是否在乘车"""
        return self.player.isInVehicle

    @property
    def entity(self):
        """在车中则返回载具对象,否则返回角色对象"""
        return self.vehicle if self.isInVehicle else self.player

    @property
    def ped_pool(self):
        """角色池"""
        return self.models.Pool(self.address.PED_POOL, self, self.Player)

    @property
    def vehicle_pool(self):
        """载具池"""
        return self.models.Pool(self.address.VEHICLE_POOL, self, self.Vehicle)

    @property
    def object_pool(self):
        """物体池"""
        return self.models.Pool(self.address.OBJECT_POOL, self, self.models.Object)

    def get_yaw(self):
        """获取偏航角"""
        PI = math.pi
        HALF_PI = PI / 2
        yaw = self.player.rotation
        yaw += HALF_PI
        if yaw > PI:
            yaw += PI * 2
        return yaw

    def get_camera_rot(self):
        """获取摄像机朝向参数 (pitch, yaw, roll)"""
        yaw = self.get_yaw()
        return (math.cos(yaw), math.sin(yaw), 0.1)

    def get_front_coord(self, delta=5):
        """获取前面一点的坐标"""
        yaw = self.get_yaw()

        x = math.cos(yaw)
        y = math.sin(yaw)
        coord = self.player.coord.values()
        coord[0] += x * delta
        coord[1] += y * delta
        return coord

    def get_cam_front_coord(self, delta=5):
        """根据摄像机朝向获取前面一点的坐标"""
        rot = self.get_camera_rot()
        coord = self.player.coord.values()
        coord[0] += rot[0] * delta
        coord[1] += rot[1] * delta
        return coord

    def go_forward(self, _=None, rate=0):
        """前进"""
        rate = rate or self.GO_FORWARD_COORD_RATE

        yaw = self.get_yaw()
        x = math.cos(yaw)
        y = math.sin(yaw)

        entity = self.entity
        coord = entity.coord.values()
        coord[0] += x * rate
        coord[1] += y * rate

        entity.coord = coord

    def speed_up(self, _=None, rate=0):
        """弹射起步"""
        speed_rate = rate or getattr(self, 'SAFE_SPEED_RATE', 0.5)

        yaw = self.get_yaw()
        x = math.cos(yaw)
        y = math.sin(yaw)

        entity = self.entity
        speed = entity.speed.values()
        speed[0] += x * speed_rate
        speed[1] += y * speed_rate

        if not self.isInVehicle:
            safe_speed_up = getattr(self, 'SAFE_SPEED_UP', 0.2)
            speed[2] = safe_speed_up
            self.raise_up(speed=safe_speed_up)
            time.sleep(0.1)
        entity.speed = speed

    def raise_up(self, _=None, speed=0):
        """上天(有速度)"""
        self.entity.speed[2] = speed or self.RAISE_UP_SPEED

    def go_down(self, _=None, speed=0):
        """向下(有速度)"""
        self.entity.speed[2] = -(speed or self.GO_DOWN_SPEED)

    def to_up(self, _=None, delta=0):
        """往上(无速度)"""
        self.entity.coord[2] += delta or self.TO_UP_DELTA

    def to_down(self, _=None, delta=0):
        """向下(无速度)"""
        self.entity.coord[2] -= delta or self.TO_DOWN_DELTA

    def stop(self, _=None):
        """停止移动"""
        self.entity.stop()

    def restore_hp(self, _=None):
        """恢复HP"""
        if self.isInVehicle:
            self.vehicle.hp = 1000
            self.vehicle_fix(self.vehicle)
        else:
            self.player.hp = 200
            self.player.ap = 200

    def restore_hp_large(self, _=None):
        """恢复大量HP"""
        if self.isInVehicle:
            self.vehicle.hp = 2000
            self.vehicle_fix(self.vehicle)
        else:
            self.player.hp = 999
            self.player.ap = 999

    def vehicle_fix(self, vehicle):
        """修车"""
        pass

    def from_player_coord(self, btn):
        """车坐标从人坐标取值"""
        self.vehicle_coord_view.input_value = self.coord_view.input_value

    def from_vehicle_coord(self, btn):
        """人坐标从车坐标取值"""
        self.coord_view.input_value = self.vehicle_coord_view.input_value

    def go_prev_pos(self, _=None):
        """瞬移到上一处地点"""
        view = self.vehicle_coord_view if self.isInVehicle else self.coord_view
        view.listbox.prev()
        view.write()

    def go_next_pos(self, _=None):
        """瞬移到下一处地点"""
        view = self.vehicle_coord_view if self.isInVehicle else self.coord_view
        view.listbox.next()
        view.write()

    def iter_cam_dir_coords(self, count, space, first_double=False):
        """往视角方向迭代坐标
        :param first_double: 第一个坐标是否是双倍距离
        """
        cam_x, cam_y, cam_z = self.get_camera_rot()
        offset = (cam_x * space, cam_y * space, cam_z * space)
        coord = self.player.get_offset_coord_m(offset)
        if first_double:
            coord += offset

        for i in range(count):
            yield coord
            coord += offset

    def get_peds(self):
        """获取角色池中的角色列表"""
        return iter(self.ped_pool)

    def get_near_peds(self, distance=100):
        """获取附近的人"""
        coord = self.player.coord.values()
        myaddr = self.player.addr
        for p in self.get_peds():
            if p.hp > 0 and p.coord[2] > 0 and p.distance(coord) <= distance and p.addr != myaddr:
                yield p

    def get_vehicles(self):
        """载具池中的载具列表"""
        return iter(self.vehicle_pool)

    def get_near_vehicles(self, distance=100):
        """获取附近的载具"""
        coord = self.player.coord.values()
        mycar = self.vehicle
        myaddr = mycar.addr if mycar else 0
        for v in self.get_vehicles():
            if v.coord[2] > 0 and v.distance(coord) <= distance and v.addr != myaddr:
                yield v

    def collect_vehicles(self):
        """暂存附近的载具"""
        self._vehicles = self.get_near_vehicles()

    def next_collected_vehicle(self, is_retry=False, recollect=False):
        """获取下一辆暂存的载具"""
        vehicles = None if recollect else getattr(self, '_vehicles', None)
        flag = False
        if vehicles:
            try:
                if getattr(self, 'iter_vehicle_locked', False):
                    vehicle = getattr(self, 'last_iter_vehicle', None)
                    if vehicle:
                        flag = True

                if not flag:
                    vehicle = next(vehicles)
                    self.last_iter_vehicle = vehicle
                    flag = True
            except StopIteration:
                if is_retry:
                    # 大概是附近没有车了
                    return

        if not flag:
            self.collect_vehicles()
            return self.next_collected_vehicle(is_retry=True)

        return vehicle

    def iter_vehicle_lock(self, _=None):
        """锁住载具迭代(接下来只操作上一个返回的已收集载具)"""
        self.iter_vehicle_locked = not getattr(self, 'iter_vehicle_locked', False)

    def kill_near_peds(self, _=None):
        """杀死附近的人"""
        for p in self.get_near_peds():
            p.hp = 0

    def near_vehicles_boom(self, _=None):
        """摧毁附近的载具"""
        for v in self.get_near_vehicles():
            v.hp = 0

    def near_vehicles_down(self, _=None):
        """获取附近的载具下陷"""
        for v in self.get_near_vehicles():
            v.coord[2] -= 0.7

    def near_vehicles_to_front(self, _=None, zinc=0):
        """ 获取附近的载具移到眼前
        :param zinc: 下一个目标的z坐标增加
        """
        coord = self.get_front_coord()
        for p in self.get_near_vehicles():
            p.coord = coord
            if zinc:
                coord[2] += zinc

    def near_peds_to_front(self, _=None, zinc=0):
        """附近的人移到眼前"""
        coord = self.get_front_coord()
        for p in self.get_near_peds():
            p.coord = coord
            if zinc:
                coord[2] += zinc

    def jump_on_vehicle(self, _=None):
        """跳上附近的一辆行驶中的车"""
        for v in self.get_near_vehicles():
            if v.driver:
                v.stop()
                coord = v.coord.values()
                coord[2] += 1
                self.entity.coord = coord
                break

    def near_vehicles_flip(self, _=None):
        """附近的载具上下翻转"""
        for v in self.get_near_vehicles():
            v.flip()

    def near_vehicle_unlock(self, _=None):
        """附近的载具解锁"""
        for v in self.get_near_vehicles():
            v.unlock_door()

    def near_peds_fly(self, _=None):
        """附近的人上天"""
        fly_speed = getattr(self, 'FLY_SPEED', 1)
        for p in self.get_near_peds():
            p.speed[2] = fly_speed

    def near_vehicles_fly(self, _=None):
        """获取附近的载具上天"""
        fly_speed = getattr(self, 'FLY_SPEED', 1)
        for v in self.get_near_vehicles():
            v.coord[2] += fly_speed
            v.speed[2] = fly_speed

    def near_fly(self, _=None):
        """获取附近的人/载具上天"""
        self.near_peds_fly()
        self.near_vehicles_fly()

    def vehicle_flip(self, _=None):
        """当前的载具翻转"""
        self.last_vehicle.flip()

    def call_vehicle(self, _=None):
        """召唤上一辆车回来"""
        car = self.last_vehicle
        if car:
            car.coord = self.get_front_coord()

    def go_vehicle(self, _=None):
        """回到上一辆车旁边"""
        car = self.last_vehicle
        if car:
            coord = car.coord.values()
            coord[2] += 5
            self.player.coord = coord

    def bring_one_vehicle(self, _=None):
        """ 把一辆车移到眼前 """
        vehicle = self.next_collected_vehicle()

        if not vehicle:
            return

        cam_x, cam_y, cam_z = self.get_camera_rot()
        coord = self.player.coord.values()
        coord[0] += cam_x * 5
        coord[1] += cam_y * 5
        coord[2] += cam_z * 5
        vehicle.stop()
        vehicle.coord = coord

    def vehicle_lock_door(self, _=None, lock=True):
        """锁车门"""
        vehicle = self.last_vehicle
        if vehicle:
            if lock:
                vehicle.lock_door()
            else:
                vehicle.unlock_door()

    def vehicle_toggle_door(self, tb):
        """切换锁车状态"""
        self.vehicle_lock_door(lock=tb.checked)

    # ----------------------------------------------------------------------
    # MARKER
    # ----------------------------------------------------------------------
    def get_blips(self, color=None, types=None, sprite=None):
        """获取所有标记"""
        Marker = self.models.Marker
        addr = self.address.BLIP_LIST
        it = Marker(addr, self)

        for i in range(self.MARKER_RANGE):
            blipType = it.blipType
            if (it.blipType and (types is None or blipType in types) and (color is None or it.color is color)
                    and (sprite is None or it.sprite is sprite)):
                yield Marker(it.addr, self)
            it.next()

    def get_first_blip(self, *args, **kwargs):
        try:
            return next(self.get_blips(*args, **kwargs))
        except StopIteration:
            pass

    def get_target_blips(self, color=None, types=None, sprite=0):
        """获取目标的所有标记"""
        return self.get_blips(color, types or self.models.Marker.AVAILABLE_TYPE)

    def recal_markers(self, _=None):
        """重新获取人/车标记点"""
        self._markers = tuple(self.get_target_blips())
        self._marker_index = 0

    def go_next_marker(self, _=None):
        """到下一处 人/车标记点"""
        if not hasattr(self, '_markers'):
            self.recal_markers()

        while True:
            try:
                entity = self._markers[self._marker_index].entity
            except IndexError:
                self.recal_markers()
                return
            if entity:
                self.entity.coord = entity.coord
                break
            self._marker_index += 1

    def move_marker_to_front(self, _=None, zinc=0):
        """人/车标记点目标移到眼前"""
        if not hasattr(self, '_markers'):
            self.recal_markers()

        moved_car_addr = []
        coord = self.get_front_coord()

        for marker in self._markers:
            entity = marker.entity
            if isinstance(entity, self.Player):
                car = entity.vehicle
                if car and car.hp > 1:
                    if car.addr not in moved_car_addr:
                        moved_car_addr.append(car.addr)
                        car.coord = coord
                else:
                    entity.coord = coord
            elif isinstance(entity, self.Vehicle):
                entity.coord = coord
            if zinc:
                coord[2] += zinc

    def teleport_to_blip(self, blip):
        """瞬移到指定标记"""
        if blip:
            blipType = blip.blipType
            if blipType is self.models.Marker.MARKER_TYPE_COORDS:
                coord = blip.coord
            else:
                entity = blip.entity
                self.en = entity
                if entity:
                    coord = entity.coord
                else:
                    return False
            self.entity.coord = coord
            return True
        return False

    def teleport_to_destination(self, _=None, color=None, types=None):
        """瞬移到目的地"""
        Marker = self.models.Marker

        if color is None:
            color = getattr(self, '_last_dest_color', self.DEST_DEFAULT_COLOR)
        else:
            self._last_dest_color = color

        if types is None:
            types = (Marker.MARKER_TYPE_CAR, Marker.MARKER_TYPE_PED, Marker.MARKER_TYPE_COORDS)

        for blip in self.get_blips(color, types):
            if blip.sprite is 0:
                if self.teleport_to_blip(blip):
                    break

    def get_selected_vehicle_model(self):
        """获取刷车器选中的载具模型"""
        return getattr(self, 'spwan_vehicle_id', None)

    def spawn_choosed_vehicle(self, _=None, coord=None):
        """生成选中的载具"""
        model = self.get_selected_vehicle_model()
        if model:
            return self.spawn_vehicle(model, coord)

    def on_spawn_vehicle_id_change(self, lb):
        """刷车器listbox回调"""
        self.spwan_vehicle_id = self.VEHICLE_LIST[lb.index][1]

    def spawn_vehicle_id_prev(self, _=None):
        """选中上一个载具"""
        pos = self.spawn_vehicle_id_view.index
        if pos == 0:
            pos = len(self.VEHICLE_LIST)
        self.spawn_vehicle_id_view.setSelection(pos - 1, True)

    def spawn_vehicle_id_next(self, _=None):
        """选中下一个载具"""
        pos = self.spawn_vehicle_id_view.index
        if pos == len(self.VEHICLE_LIST) - 1:
            pos = -1
        self.spawn_vehicle_id_view.setSelection(pos + 1, True)

    def lock_door(self, _=None):
        """锁最近使用的载具的车门"""
        vehicle = self.last_vehicle
        if vehicle:
            vehicle.lock_door()

    def unlock_door(self, _=None):
        """解锁最近使用的载具的车门"""
        vehicle = self.last_vehicle
        if vehicle:
            vehicle.unlock_door()

    def launch_entity(self, entitys, need_set_coord=True):
        """朝前方发射载具"""
        if not hasattr(entitys, '__iter__'):
            entitys = (entitys,)

        for entity in entitys:
            cam_x, cam_y, cam_z = self.get_camera_rot()
            speed_rate = getattr(self, 'SLING_SPEED_RATE', 3)
            speed = (cam_x * speed_rate, cam_y * speed_rate, cam_z * speed_rate)
            if need_set_coord:
                coord_up = getattr(self, 'SLING_COORD_UP', 1)
                coord_delta = getattr(self, 'SLING_COORD_DELTA', 5)
                if self.isInVehicle:
                    coord_delta *= 1.5
                coord = self.player.coord.values()
                coord[0] += cam_x * coord_delta
                coord[1] += cam_y * coord_delta
                coord[2] += cam_z * coord_delta + coord_up
                entity.stop()
                entity.coord = coord
            entity.speed = speed

    def sling(self, _=None, recollect=False):
        """载具发射台"""
        vehicle = self.next_collected_vehicle(recollect=recollect)
        if vehicle:
            self.launch_entity(vehicle)
            self._last_launch_vehicle = vehicle

    def spawn_and_launch(self, _=None, recreate=False):
        """生产载具并发射"""
        vehicle = None
        if not recreate:
            vehicle = getattr(self, '_last_spawn_and_launch_vehicle', None)
        if not vehicle or vehicle.model_id != self.get_selected_vehicle_model():
            coord_delta = getattr(self, 'SLING_COORD_DELTA', 5)
            if self.isInVehicle:
                coord_delta *= 1.5
            vehicle = self.spawn_choosed_vehicle(coord=self.get_cam_front_coord(coord_delta))
            self._last_spawn_and_launch_vehicle = vehicle
        if vehicle:
            self.launch_entity(vehicle)
            self._last_launch_vehicle = vehicle

    def explode_last_launch_vehicle(self, _=None):
        """爆破上次发射的载具"""
        vehicle = getattr(self, '_last_launch_vehicle', None)
        if vehicle:
            vehicle.explode()

    def clear_wanted_level(self, _=None):
        """清除通缉等级"""
        self.set_wanted_level(0)

    def set_wanted_level(self, value):
        self.player.wanted_level = value

    def explode_art(self, _=None, count=10):
        """焰之炼金术 (向前生成数个爆炸)"""
        distance = (getattr(self, 'EXPLODE_DISTANCE_VEHICLE', 8) if self.isInVehicle
            else getattr(self, 'EXPLODE_DISTANCE', 6))
        for coord in self.iter_cam_dir_coords(count, distance, True):
            self.create_explosion(coord)

    def g3l2json(self, _=None):
        """g3l坐标转json"""
        path = fefactory_api.choose_file("选择要读取的文件", wildcard='*.g3l')
        if path:
            with open(path) as file:
                if not file.readline().strip() == '[Locks]':
                    fefactory_api.alert('不支持的格式')
                    return

                coord = [0, 0, 0]
                datas = []
                while True:
                    line = file.readline()
                    if not line:
                        break
                    line = line.strip()
                    if line.startswith('x='):
                        coord[0] = float(line[2:])
                    elif line.startswith('y='):
                        coord[1] = float(line[2:])
                    elif line.startswith('z='):
                        coord[2] = float(line[2:])
                    elif line.startswith('desc='):
                        datas.append({'name': line[5:], 'value': tuple(coord)})

            jsonpath = path[:path.rfind('.') + 1] + 'json'
            with open(jsonpath, 'w', encoding="utf-8") as file:
                json.dump(datas, file, ensure_ascii=False)

            fefactory_api.alert('转换成功: ' + jsonpath)

    def render_common_button(self):
        ui.Button("杀掉附近的人", onclick=self.kill_near_peds)
        ui.Button("附近的车摧毁", onclick=self.near_vehicles_boom)
        ui.Button("附近的车下陷", onclick=self.near_vehicles_down)
        ui.Button("附近的车移到眼前", onclick=self.near_vehicles_to_front)
        ui.Button("附近的人移到眼前", onclick=self.near_peds_to_front)
        ui.Button("附近的车上天", onclick=self.near_vehicles_fly)
        ui.Button("附近的人上天", onclick=self.near_peds_fly)
        ui.Button("附近的车翻转", onclick=self.near_vehicles_flip)
        ui.Button("跳上一辆车", onclick=self.jump_on_vehicle)
        ui.Button("召唤上一辆车回来", onclick=self.call_vehicle)
        ui.Button("回到上一辆车旁边", onclick=self.go_vehicle)
        ui.Button("附近的载具解锁", onclick=self.near_vehicle_unlock)
        ui.Button("锁定载具迭代", onclick=self.iter_vehicle_lock)

    def render_common_text(self):
        ui.Text("生产选中的载具: alt+v\n"
            "选中上一个载具: alt+[\n"
            "选中下一个载具: alt+]\n"
            "向前穿墙: alt+w\n"
            "向前穿墙大: alt+shift+w\n"
            "弹射起步: alt+m\n"
            "上天(有速度): alt+空格\n"
            "往上(无速度): alt+.\n"
            "下坠: alt+shift+空格\n"
            "停止移动: alt+x\n"
            "恢复HP: alt+h\n"
            "恢复大量HP: alt+shift+h\n"
            "跳上一辆车: alt+j\n"
            "附近的人上天: alt+f\n"
            "附近的人和载具上天: alt+shift+f\n"
            "自己的载具翻转: alt+k\n"
            "附近的载具翻转: alt+shift+k\n"
            "附近的人移到眼前: alt+shift+p\n"
            "附近的载具移到眼前: alt+p\n"
            "瞬移到上一个地点: alt+shift+,\n"
            "瞬移到下一个地点: alt+shift+.\n"
            "重新获取雷达上标记的目标: alt+'\n"
            "瞬移到下一个标记目标处: alt+/\n"
            "把获取到的标记目标移到眼前: alt+shift+/\n"
            "上一辆车锁门: alt+l\n"
            "上一辆车解锁: alt+shift+l\n"
            "载具发射台(扫描附件的车,依次把一辆车发射出去): alt+d\n"
            "载具发射台(重新扫描): alt+shift+d\n"
            "生产载具并发射: alt+a\n"
            "爆破上次发射的载具: alt+shift+a\n"
            "把一辆车移到眼前: alt+b\n"
            "清除通缉等级: alt+0\n"
            "红莲之炼金术 (向前生成数个爆炸): alt+`\n"
            "红莲之炼金术 (长): alt+shift+`\n"
            "瞬移到目的地: alt+1\n"
            "自定义临时热键(执行tool.cfn): alt+c")

    def get_common_hotkeys(self):
        return (
            (VK.MOD_ALT, VK.W, self.go_forward),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.W, partial(self.go_forward, rate=10), 'go_forward_large'),
            (VK.MOD_ALT, VK.M, self.speed_up),
            (VK.MOD_ALT, VK.SPACE, self.raise_up),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.SPACE, self.go_down),
            (VK.MOD_ALT, VK.getCode(','), self.to_up),
            (VK.MOD_ALT, VK.getCode('.'), self.to_down),
            (VK.MOD_ALT, VK.X, self.stop),
            (VK.MOD_ALT, VK.H, self.restore_hp),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.H, self.restore_hp_large),
            (VK.MOD_ALT, VK.J, self.jump_on_vehicle),
            (VK.MOD_ALT, VK.F, self.near_peds_fly),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.F, self.near_fly),
            (VK.MOD_ALT, VK.K, self.vehicle_flip),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.K, self.near_vehicles_flip),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.P, self.near_peds_to_front),
            (VK.MOD_ALT, VK.P, self.near_vehicles_to_front),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.getCode(','), self.go_prev_pos),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.getCode('.'), self.go_next_pos),
            (VK.MOD_ALT, VK.getCode("'"), self.recal_markers),
            (VK.MOD_ALT, VK.getCode('/'), self.go_next_marker),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.getCode('/'), self.move_marker_to_front),
            (VK.MOD_ALT, VK.L, self.lock_door),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.L, self.unlock_door),
            (VK.MOD_ALT, VK.D, self.sling),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.D, partial(self.sling, recollect=True), 'resling'),
            (VK.MOD_ALT, VK.A, self.spawn_and_launch),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.A, partial(self.spawn_and_launch, recreate=True), 'respawn_and_launch'),
            (VK.MOD_ALT, VK.Z, self.explode_last_launch_vehicle),
            (VK.MOD_ALT, VK.B, self.bring_one_vehicle),
            (VK.MOD_ALT, VK._0, self.clear_wanted_level),
            (VK.MOD_ALT, VK.getCode('`'), self.explode_art),
            (VK.MOD_ALT | VK.MOD_SHIFT, VK.getCode('`'), partial(self.explode_art, count=24), 'explode_art_long'),
            (VK.MOD_ALT, VK.Q, partial(self.explode_art, count=1), 'explode_art_one'),
            (VK.MOD_ALT, VK._1, self.teleport_to_destination),
            (VK.MOD_ALT, VK.getCode('['), self.spawn_vehicle_id_prev),
            (VK.MOD_ALT, VK.getCode(']'), self.spawn_vehicle_id_next),
            (VK.MOD_ALT, VK.V, self.spawn_choosed_vehicle),
            (VK.MOD_ALT, VK.C, self.custom_hotkey),
        )