enemyFighterPower = (enemyFleetShips, enemyShipSlots, enemySlotSizes, forLbas) => {
        var totalPower = false;
        var totalCapacity = 0;
        var noAirPowerCapacity = 0;
        var reconCapacity = 0;
        const exceptions = {};
        // no ship IDs
        if(!enemyFleetShips) {
            exceptions.ship = null;
            return [totalPower, totalCapacity, exceptions];
        }
        $.each(enemyFleetShips, (shipIdx, shipId) => {
            // ignore -1 placeholder
            if(!shipId || shipId < 0) {
                return;
            }
            const shipMst = KC3Master.isAbyssalShip(shipId) ?
                KC3Master.abyssalShip(shipId, true) : KC3Master.ship(shipId);
            // no ship master data
            if(!shipMst) {
                exceptions[shipId] = null;
                return;
            }
            let shipSlots = (enemyShipSlots || [])[shipIdx] || shipMst.kc3_slots;
            // no slot gear IDs
            if(!Array.isArray(shipSlots)) {
                exceptions[shipId] = {};
                return;
            }
            // mainly remove -1 placeholders
            shipSlots = shipSlots.filter(id => id > 0);
            for(let slotIdx = 0; slotIdx < shipSlots.length; slotIdx++) {
                const gearId = shipSlots[slotIdx];
                const gearMst = KC3Master.slotitem(gearId);
                // no gear master data
                if(!gearMst) {
                    exceptions[shipId] = exceptions[shipId] || {};
                    exceptions[shipId][gearId] = null;
                    continue;
                }
                // for LBAS battle, recon planes participate, and their fighter power may be counted
                if(KC3GearManager.antiAirFighterType2Ids.includes(gearMst.api_type[2])
                    || (!!forLbas && KC3GearManager.antiAirLandBaseFighterType2Ids.includes(gearMst.api_type[2]))
                    // Autogyro, AS Patrol participate in stage1 since Hayabusa, but 0 fight power for others
                    || [25, 26].includes(gearMst.api_type[2])) {
                    const aaStat = gearMst.api_tyku || 0;
                    const capacity = ((enemySlotSizes || [])[shipIdx] || shipMst.api_maxeq || [])[slotIdx];
                    if(capacity !== undefined) {
                        if(aaStat > 0) {
                            totalCapacity += capacity;
                            totalPower += Math.floor(Math.sqrt(capacity) * aaStat);
                        } else {
                            noAirPowerCapacity += capacity;
                        }
                    } else {
                        // no slot maxeq (capacity)
                        exceptions[shipId] = exceptions[shipId] || {};
                        exceptions[shipId][gearId] = aaStat;
                    }
                } else if(KC3GearManager.antiAirLandBaseFighterType2Ids.includes(gearMst.api_type[2])) {
                    // sum other planes not participate in normal air battle but LBAS battle,
                    // non plane slot, seaplane fighters/bombers will not be dropped here
                    const capacity = ((enemySlotSizes || [])[shipIdx] || shipMst.api_maxeq || [])[slotIdx];
                    if(capacity !== undefined) {
                        reconCapacity += capacity;
                    } else {
                        exceptions[shipId] = exceptions[shipId] || {};
                        exceptions[shipId][gearId] = "lbas";
                    }
                }
            }
        });
        return [totalPower, totalCapacity, noAirPowerCapacity, reconCapacity, exceptions];
    }
Get battle opponent's fighter power only based on master data.

@param {Array}  enemyFleetShips - master ID array of opponent fleet ships.
@param {Array[]} enemyShipSlots - master ID array of equip slots, optional.
                                  length should be the same with enemyFleetShips.
@param {Array[]} enemySlotSizes - capacities of equip slots, optional, same length either.
@param {boolean} forLbas - specify true if power for LBAS battle requested.
@return {Array} a tuple contains [
          computed fighter power (without improvement and proficiency bonus),
          sum of known slot capacity,
          sum of slot capacity without air power,
          sum of slot capacity with recon/lbaa/etc planes equipped,
          exception map indicates which ship or gear missing required data:
            {shipId: null || {gearId: null || aaStat || 'lbas'}}
        ]
@see Fleet, Ship, Gear classes to compute fighter power of player fleet.
Useful for estimating total plane slots of 1 new enemy, but to estimate slots distribution:
 * For fighter slots, have to gather at least 1 record with stage1 enemy 0 loss
 * For bomber slots, gather edge data via AA shotdown: https://x.com/Croshadow_/status/1167439355935215616
 * For final distribution, gather edge data for AP/AS/AS+ to confirm enemy's fighter power