@gaia-project/engine
Advanced tools
Comparing version 4.8.39 to 4.8.40
{ | ||
"name": "@gaia-project/engine", | ||
"version": "4.8.39", | ||
"version": "4.8.40", | ||
"description": "Javascript engine for project gaia", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -6,3 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.possibleShipMovements = exports.possibleShipActions = exports.possibleShips = exports.shipsInHex = exports.TRADE_COST = void 0; | ||
exports.possibleShipMovements = exports.possibleShipActions = exports.tradeCost = exports.tradeRewards = exports.baseTradeReward = exports.tradeBonus = exports.possibleShips = exports.shipsInHex = exports.tradeOptions = exports.TRADE_COST = void 0; | ||
const enums_1 = require("../enums"); | ||
@@ -15,2 +15,43 @@ const reward_1 = __importDefault(require("../reward")); | ||
exports.TRADE_COST = 3; | ||
exports.tradeOptions = [ | ||
{ | ||
building: enums_1.Building.Mine, | ||
domestic: true, | ||
free: true, | ||
base: reward_1.default.parse("1o"), | ||
bonus: [], | ||
}, | ||
{ | ||
building: enums_1.Building.Mine, | ||
base: [], | ||
bonus: reward_1.default.parse("1c,1o"), | ||
build: enums_1.Building.CustomsPost, | ||
}, | ||
{ | ||
building: enums_1.Building.TradingStation, | ||
base: reward_1.default.parse("5c"), | ||
bonus: reward_1.default.parse("3c,1pw"), | ||
}, | ||
{ | ||
building: enums_1.Building.ResearchLab, | ||
base: reward_1.default.parse("2k"), | ||
bonus: reward_1.default.parse("1k"), | ||
}, | ||
{ | ||
building: enums_1.Building.Academy1, | ||
base: reward_1.default.parse("2k"), | ||
bonus: reward_1.default.parse("1k"), | ||
researchAdvancementBonus: true, | ||
}, | ||
{ | ||
building: enums_1.Building.PlanetaryInstitute, | ||
base: reward_1.default.parse("1t,2pw"), | ||
bonus: reward_1.default.parse("4pw"), | ||
}, | ||
{ | ||
building: enums_1.Building.Colony, | ||
base: reward_1.default.parse("3vp"), | ||
bonus: reward_1.default.parse("2vp"), | ||
}, | ||
]; | ||
function shipsInHex(location, data) { | ||
@@ -72,70 +113,34 @@ return data.players.flatMap((p) => p.data.ships).filter((s) => s.location === location); | ||
} | ||
function tradeCost(engine, player) { | ||
return new reward_1.default(engine.player(player).data.tradeCost(), enums_1.Resource.ChargePower); | ||
} | ||
function tradeUnits(engine, player, building) { | ||
const p = engine.player(player); | ||
function tradeBonus(data, option) { | ||
let rewards = []; | ||
for (let i = 0; i < p.data.tradeBonus; i++) { | ||
rewards = reward_1.default.merge(rewards.concat(tradeUnit(building))); | ||
for (let i = 0; i < data.tradeBonus; i++) { | ||
rewards = reward_1.default.merge(rewards.concat(option.bonus)); | ||
} | ||
return rewards; | ||
} | ||
exports.tradeBonus = tradeBonus; | ||
function isFurther(guest, host) { | ||
return enums_1.ResearchField.values(enums_1.Expansion.All).filter((f) => host[f] > guest[f]).length; | ||
} | ||
function baseTradeReward(building, guest, host, engine) { | ||
switch (building) { | ||
//own Mine | ||
case enums_1.Building.Mine: | ||
return reward_1.default.parse("1o"); | ||
case enums_1.Building.TradingStation: | ||
return reward_1.default.parse("5c"); | ||
case enums_1.Building.ResearchLab: | ||
return reward_1.default.parse("2k"); | ||
case enums_1.Building.Academy1: | ||
case enums_1.Building.Academy2: | ||
return [ | ||
new reward_1.default(Math.max(2, isFurther(engine.player(guest).data.research, engine.player(host).data.research)), enums_1.Resource.Knowledge), | ||
]; | ||
case enums_1.Building.PlanetaryInstitute: | ||
return reward_1.default.parse("1t,2pw"); | ||
case enums_1.Building.Colony: | ||
return reward_1.default.parse("3vp"); | ||
function baseTradeReward(option, guest, host) { | ||
if (option.researchAdvancementBonus) { | ||
const base = option.base[0]; | ||
return [new reward_1.default(Math.max(base.count, isFurther(guest.research, host.research)), base.type)]; | ||
} | ||
throw new Error("unknown trade bonus: " + building); | ||
return option.base; | ||
} | ||
function tradeUnit(building) { | ||
switch (building) { | ||
case enums_1.Building.Mine: | ||
//foreign mine | ||
return reward_1.default.parse("1c,1o"); | ||
case enums_1.Building.TradingStation: | ||
return reward_1.default.parse("3c,1pw"); | ||
case enums_1.Building.ResearchLab: | ||
case enums_1.Building.Academy1: | ||
case enums_1.Building.Academy2: | ||
return reward_1.default.parse("1k"); | ||
case enums_1.Building.PlanetaryInstitute: | ||
return reward_1.default.parse("4pw"); | ||
case enums_1.Building.Colony: | ||
return reward_1.default.parse("2vp"); | ||
} | ||
throw new Error("unknown trade bonus: " + building); | ||
} | ||
function possibleCustomsPost(engine, h, player) { | ||
exports.baseTradeReward = baseTradeReward; | ||
function possibleBuilding(engine, h, player, building, rewards, cost) { | ||
const p = engine.player(player); | ||
const b = enums_1.Building.CustomsPost; | ||
const units = tradeUnits(engine, player, enums_1.Building.Mine); | ||
const canBuildAfterTrade = p.canBuild(engine.map, h, h.data.planet, b, engine.isLastRound, engine.replay, { | ||
addedCost: reward_1.default.negative(units), | ||
const canBuildAfterTrade = p.canBuild(engine.map, h, h.data.planet, building, engine.isLastRound, engine.replay, { | ||
addedCost: reward_1.default.negative(rewards), | ||
}); | ||
if (canBuildAfterTrade) { | ||
canBuildAfterTrade.cost = p.board.cost(b, false); | ||
const availableBuilding = buildings_1.newAvailableBuilding(b, h, canBuildAfterTrade, false); | ||
canBuildAfterTrade.cost = p.board.cost(building, false); | ||
const availableBuilding = buildings_1.newAvailableBuilding(building, h, canBuildAfterTrade, false); | ||
return [ | ||
{ | ||
...availableBuilding, | ||
tradeCost: tradeCost(engine, player).toString(), | ||
rewards: units.toString(), | ||
tradeCost: cost.toString(), | ||
rewards: rewards.toString(), | ||
}, | ||
@@ -146,52 +151,53 @@ ]; | ||
} | ||
function totalTradeReward(h, player, engine) { | ||
function tradeRewards(option, guest, host) { | ||
return reward_1.default.merge(baseTradeReward(option, guest, host).concat(tradeBonus(guest, option))); | ||
} | ||
exports.tradeRewards = tradeRewards; | ||
function tradeCost(guest, option) { | ||
return option.free ? new reward_1.default(0, enums_1.Resource.ChargePower) : guest.tradeCost(); | ||
} | ||
exports.tradeCost = tradeCost; | ||
function tradeLocations(h, player, engine) { | ||
const p = engine.player(player); | ||
if (h.hasStructure() && !h.tradeTokens.some((t) => t === player) && !h.customPosts.some((t) => t === player)) { | ||
const building = h.data.building; | ||
const host = h.data.player; | ||
if (host !== player) { | ||
const canTrade = engine.player(player).data.canPay([tradeCost(engine, player)]); | ||
if (!canTrade) { | ||
return []; | ||
const host = engine.player(h.data.player).data; | ||
const guest = p.data; | ||
const domestic = h.data.player === player; | ||
const option = exports.tradeOptions.find((o) => o.building === building && !!o.domestic === domestic); | ||
if (option) { | ||
const cost = tradeCost(guest, option); | ||
if (engine.player(player).data.canPay([cost])) { | ||
const rewards = tradeRewards(option, guest, host); | ||
if (option.build) { | ||
return possibleBuilding(engine, h, player, option.build, rewards, cost); | ||
} | ||
else { | ||
return [ | ||
{ | ||
coordinates: h.toString(), | ||
tradeCost: cost.toString(), | ||
rewards: rewards.toString(), | ||
}, | ||
]; | ||
} | ||
} | ||
if (building === enums_1.Building.Mine) { | ||
return possibleCustomsPost(engine, h, player); | ||
} | ||
else { | ||
return [ | ||
{ | ||
coordinates: h.toString(), | ||
tradeCost: tradeCost(engine, player).toString(), | ||
rewards: reward_1.default.merge(baseTradeReward(building, player, host, engine).concat(tradeUnits(engine, player, building))).toString(), | ||
}, | ||
]; | ||
} | ||
} | ||
else if (building === enums_1.Building.Mine) { | ||
return [ | ||
{ | ||
coordinates: h.toString(), | ||
rewards: baseTradeReward(building, player, host, engine).toString(), | ||
}, | ||
]; | ||
} | ||
} | ||
return []; | ||
} | ||
function possibleTradeShipActions(engine, ship, shipLocation) { | ||
return possibleShipActionsOfType(engine, ship, shipLocation, types_1.ShipAction.Trade, true, (h) => totalTradeReward(h, ship.player, engine)); | ||
} | ||
function possibleColonyShipActions(engine, ship, shipLocation, requireTemporaryStep) { | ||
return possibleShipActionsOfType(engine, ship, shipLocation, types_1.ShipAction.BuildColony, !requireTemporaryStep, (h) => { | ||
const player = engine.player(ship.player); | ||
const existingBuilding = h.buildingOf(player.player); | ||
if (h.hasPlanet() && (!h.occupied() || existingBuilding === enums_1.Building.GaiaFormer)) { | ||
const check = player.canBuild(engine.map, h, h.data.planet, enums_1.Building.Colony, engine.isLastRound, engine.replay, { | ||
existingBuilding, | ||
}); | ||
if (check) { | ||
return [buildings_1.newAvailableBuilding(enums_1.Building.Colony, h, check, false)]; | ||
} | ||
function colonyActions(engine, ship, h) { | ||
const player = engine.player(ship.player); | ||
const existingBuilding = h.buildingOf(player.player); | ||
if (h.hasPlanet() && | ||
h.data.planet !== enums_1.Planet.Transdim && | ||
(!h.occupied() || existingBuilding === enums_1.Building.GaiaFormer)) { | ||
const check = player.canBuild(engine.map, h, h.data.planet, enums_1.Building.Colony, engine.isLastRound, engine.replay, { | ||
existingBuilding, | ||
}); | ||
if (check) { | ||
return [buildings_1.newAvailableBuilding(enums_1.Building.Colony, h, check, false)]; | ||
} | ||
return []; | ||
}); | ||
} | ||
return []; | ||
} | ||
@@ -201,5 +207,5 @@ function possibleShipActions(engine, ship, shipLocation, requireTemporaryStep) { | ||
case enums_1.Building.ColonyShip: | ||
return possibleColonyShipActions(engine, ship, shipLocation, requireTemporaryStep); | ||
return possibleShipActionsOfType(engine, ship, shipLocation, types_1.ShipAction.BuildColony, !requireTemporaryStep, (h) => colonyActions(engine, ship, h)); | ||
case enums_1.Building.TradeShip: | ||
return possibleTradeShipActions(engine, ship, shipLocation); | ||
return possibleShipActionsOfType(engine, ship, shipLocation, types_1.ShipAction.Trade, true, (h) => tradeLocations(h, ship.player, engine)); | ||
} | ||
@@ -206,0 +212,0 @@ return []; |
@@ -512,3 +512,3 @@ "use strict"; | ||
tradeCost() { | ||
return ships_1.TRADE_COST - this.tradeDiscount; | ||
return new reward_1.default(ships_1.TRADE_COST - this.tradeDiscount, enums_1.Resource.ChargePower); | ||
} | ||
@@ -515,0 +515,0 @@ /** |
{ | ||
"name": "@gaia-project/engine", | ||
"version": "4.8.39", | ||
"version": "4.8.40", | ||
"description": "Javascript engine for project gaia", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
import Engine from "../engine"; | ||
import { Building, Command, Expansion, Player, ResearchField, Resource, Ship } from "../enums"; | ||
import { Building, Command, Expansion, Planet, Player, ResearchField, Resource, Ship } from "../enums"; | ||
import { GaiaHex } from "../gaia-hex"; | ||
import SpaceMap from "../map"; | ||
import PlayerObject from "../player"; | ||
import { ResearchProgress } from "../player-data"; | ||
import PlayerData, { ResearchProgress } from "../player-data"; | ||
import Reward from "../reward"; | ||
@@ -23,2 +23,54 @@ import { newAvailableBuilding } from "./buildings"; | ||
export type TradeOption = { | ||
building: Building; | ||
domestic?: boolean; | ||
free?: boolean; | ||
base: Reward[]; | ||
bonus: Reward[]; | ||
researchAdvancementBonus?: boolean; | ||
build?: Building; | ||
}; | ||
export const tradeOptions: TradeOption[] = [ | ||
{ | ||
building: Building.Mine, | ||
domestic: true, | ||
free: true, | ||
base: Reward.parse("1o"), | ||
bonus: [], | ||
}, | ||
{ | ||
building: Building.Mine, | ||
base: [], | ||
bonus: Reward.parse("1c,1o"), | ||
build: Building.CustomsPost, | ||
}, | ||
{ | ||
building: Building.TradingStation, | ||
base: Reward.parse("5c"), | ||
bonus: Reward.parse("3c,1pw"), | ||
}, | ||
{ | ||
building: Building.ResearchLab, | ||
base: Reward.parse("2k"), | ||
bonus: Reward.parse("1k"), | ||
}, | ||
{ | ||
building: Building.Academy1, | ||
base: Reward.parse("2k"), | ||
bonus: Reward.parse("1k"), | ||
researchAdvancementBonus: true, | ||
}, | ||
{ | ||
building: Building.PlanetaryInstitute, | ||
base: Reward.parse("1t,2pw"), | ||
bonus: Reward.parse("4pw"), | ||
}, | ||
{ | ||
building: Building.Colony, | ||
base: Reward.parse("3vp"), | ||
bonus: Reward.parse("2vp"), | ||
}, | ||
]; | ||
export function shipsInHex(location: string, data): Ship[] { | ||
@@ -97,12 +149,6 @@ return data.players.flatMap((p) => p.data.ships).filter((s) => s.location === location); | ||
function tradeCost(engine: Engine, player: Player): Reward { | ||
return new Reward(engine.player(player).data.tradeCost(), Resource.ChargePower); | ||
} | ||
function tradeUnits(engine: Engine, player: Player, building: Building): Reward[] { | ||
const p = engine.player(player); | ||
export function tradeBonus(data: PlayerData, option: TradeOption): Reward[] { | ||
let rewards = []; | ||
for (let i = 0; i < p.data.tradeBonus; i++) { | ||
rewards = Reward.merge(rewards.concat(tradeUnit(building))); | ||
for (let i = 0; i < data.tradeBonus; i++) { | ||
rewards = Reward.merge(rewards.concat(option.bonus)); | ||
} | ||
@@ -116,61 +162,30 @@ return rewards; | ||
function baseTradeReward(building: Building, guest: Player, host: Player, engine: Engine): Reward[] { | ||
switch (building) { | ||
//own Mine | ||
case Building.Mine: | ||
return Reward.parse("1o"); | ||
case Building.TradingStation: | ||
return Reward.parse("5c"); | ||
case Building.ResearchLab: | ||
return Reward.parse("2k"); | ||
case Building.Academy1: | ||
case Building.Academy2: | ||
return [ | ||
new Reward( | ||
Math.max(2, isFurther(engine.player(guest).data.research, engine.player(host).data.research)), | ||
Resource.Knowledge | ||
), | ||
]; | ||
case Building.PlanetaryInstitute: | ||
return Reward.parse("1t,2pw"); | ||
case Building.Colony: | ||
return Reward.parse("3vp"); | ||
export function baseTradeReward(option: TradeOption, guest: PlayerData, host: PlayerData): Reward[] { | ||
if (option.researchAdvancementBonus) { | ||
const base = option.base[0]; | ||
return [new Reward(Math.max(base.count, isFurther(guest.research, host.research)), base.type)]; | ||
} | ||
throw new Error("unknown trade bonus: " + building); | ||
return option.base; | ||
} | ||
function tradeUnit(building: Building): Reward[] { | ||
switch (building) { | ||
case Building.Mine: | ||
//foreign mine | ||
return Reward.parse("1c,1o"); | ||
case Building.TradingStation: | ||
return Reward.parse("3c,1pw"); | ||
case Building.ResearchLab: | ||
case Building.Academy1: | ||
case Building.Academy2: | ||
return Reward.parse("1k"); | ||
case Building.PlanetaryInstitute: | ||
return Reward.parse("4pw"); | ||
case Building.Colony: | ||
return Reward.parse("2vp"); | ||
} | ||
throw new Error("unknown trade bonus: " + building); | ||
} | ||
function possibleCustomsPost(engine: Engine, h: GaiaHex, player: Player) { | ||
function possibleBuilding( | ||
engine: Engine, | ||
h: GaiaHex, | ||
player: Player, | ||
building: Building, | ||
rewards: Reward[], | ||
cost: Reward | ||
) { | ||
const p = engine.player(player); | ||
const b = Building.CustomsPost; | ||
const units = tradeUnits(engine, player, Building.Mine); | ||
const canBuildAfterTrade = p.canBuild(engine.map, h, h.data.planet, b, engine.isLastRound, engine.replay, { | ||
addedCost: Reward.negative(units), | ||
const canBuildAfterTrade = p.canBuild(engine.map, h, h.data.planet, building, engine.isLastRound, engine.replay, { | ||
addedCost: Reward.negative(rewards), | ||
}); | ||
if (canBuildAfterTrade) { | ||
canBuildAfterTrade.cost = p.board.cost(b, false); | ||
const availableBuilding = newAvailableBuilding(b, h, canBuildAfterTrade, false); | ||
canBuildAfterTrade.cost = p.board.cost(building, false); | ||
const availableBuilding = newAvailableBuilding(building, h, canBuildAfterTrade, false); | ||
return [ | ||
{ | ||
...availableBuilding, | ||
tradeCost: tradeCost(engine, player).toString(), | ||
rewards: units.toString(), | ||
tradeCost: cost.toString(), | ||
rewards: rewards.toString(), | ||
}, | ||
@@ -182,31 +197,34 @@ ]; | ||
function totalTradeReward(h: GaiaHex, player: Player, engine: Engine): ShipActionLocation[] { | ||
export function tradeRewards(option: TradeOption, guest: PlayerData, host: PlayerData) { | ||
return Reward.merge(baseTradeReward(option, guest, host).concat(tradeBonus(guest, option))); | ||
} | ||
export function tradeCost(guest: PlayerData, option: TradeOption) { | ||
return option.free ? new Reward(0, Resource.ChargePower) : guest.tradeCost(); | ||
} | ||
function tradeLocations(h: GaiaHex, player: Player, engine: Engine): ShipActionLocation[] { | ||
const p = engine.player(player); | ||
if (h.hasStructure() && !h.tradeTokens.some((t) => t === player) && !h.customPosts.some((t) => t === player)) { | ||
const building = h.data.building; | ||
const host = h.data.player; | ||
if (host !== player) { | ||
const canTrade = engine.player(player).data.canPay([tradeCost(engine, player)]); | ||
if (!canTrade) { | ||
return []; | ||
const host = engine.player(h.data.player).data; | ||
const guest = p.data; | ||
const domestic = h.data.player === player; | ||
const option = tradeOptions.find((o) => o.building === building && !!o.domestic === domestic); | ||
if (option) { | ||
const cost = tradeCost(guest, option); | ||
if (engine.player(player).data.canPay([cost])) { | ||
const rewards = tradeRewards(option, guest, host); | ||
if (option.build) { | ||
return possibleBuilding(engine, h, player, option.build, rewards, cost); | ||
} else { | ||
return [ | ||
{ | ||
coordinates: h.toString(), | ||
tradeCost: cost.toString(), | ||
rewards: rewards.toString(), | ||
} as TradingLocation, | ||
]; | ||
} | ||
} | ||
if (building === Building.Mine) { | ||
return possibleCustomsPost(engine, h, player); | ||
} else { | ||
return [ | ||
{ | ||
coordinates: h.toString(), | ||
tradeCost: tradeCost(engine, player).toString(), | ||
rewards: Reward.merge( | ||
baseTradeReward(building, player, host, engine).concat(tradeUnits(engine, player, building)) | ||
).toString(), | ||
} as TradingLocation, | ||
]; | ||
} | ||
} else if (building === Building.Mine) { | ||
return [ | ||
{ | ||
coordinates: h.toString(), | ||
rewards: baseTradeReward(building, player, host, engine).toString(), | ||
} as TradingLocation, | ||
]; | ||
} | ||
@@ -217,27 +235,18 @@ } | ||
function possibleTradeShipActions(engine: Engine, ship: Ship, shipLocation: string): AvailableShipAction[] { | ||
return possibleShipActionsOfType(engine, ship, shipLocation, ShipAction.Trade, true, (h) => | ||
totalTradeReward(h, ship.player, engine) | ||
); | ||
} | ||
function possibleColonyShipActions( | ||
engine: Engine, | ||
ship: Ship, | ||
shipLocation: string, | ||
requireTemporaryStep: boolean | ||
): AvailableShipAction[] { | ||
return possibleShipActionsOfType(engine, ship, shipLocation, ShipAction.BuildColony, !requireTemporaryStep, (h) => { | ||
const player = engine.player(ship.player); | ||
const existingBuilding = h.buildingOf(player.player); | ||
if (h.hasPlanet() && (!h.occupied() || existingBuilding === Building.GaiaFormer)) { | ||
const check = player.canBuild(engine.map, h, h.data.planet, Building.Colony, engine.isLastRound, engine.replay, { | ||
existingBuilding, | ||
}); | ||
if (check) { | ||
return [newAvailableBuilding(Building.Colony, h, check, false)]; | ||
} | ||
function colonyActions(engine: Engine, ship: Ship, h: GaiaHex): AvailableBuilding[] { | ||
const player = engine.player(ship.player); | ||
const existingBuilding = h.buildingOf(player.player); | ||
if ( | ||
h.hasPlanet() && | ||
h.data.planet !== Planet.Transdim && | ||
(!h.occupied() || existingBuilding === Building.GaiaFormer) | ||
) { | ||
const check = player.canBuild(engine.map, h, h.data.planet, Building.Colony, engine.isLastRound, engine.replay, { | ||
existingBuilding, | ||
}); | ||
if (check) { | ||
return [newAvailableBuilding(Building.Colony, h, check, false)]; | ||
} | ||
return []; | ||
}); | ||
} | ||
return []; | ||
} | ||
@@ -253,5 +262,9 @@ | ||
case Building.ColonyShip: | ||
return possibleColonyShipActions(engine, ship, shipLocation, requireTemporaryStep); | ||
return possibleShipActionsOfType(engine, ship, shipLocation, ShipAction.BuildColony, !requireTemporaryStep, (h) => | ||
colonyActions(engine, ship, h) | ||
); | ||
case Building.TradeShip: | ||
return possibleTradeShipActions(engine, ship, shipLocation); | ||
return possibleShipActionsOfType(engine, ship, shipLocation, ShipAction.Trade, true, (h) => | ||
tradeLocations(h, ship.player, engine) | ||
); | ||
} | ||
@@ -258,0 +271,0 @@ return []; |
@@ -603,4 +603,4 @@ import { EventEmitter } from "eventemitter3"; | ||
tradeCost(): number { | ||
return TRADE_COST - this.tradeDiscount; | ||
tradeCost(): Reward { | ||
return new Reward(TRADE_COST - this.tradeDiscount, Resource.ChargePower); | ||
} | ||
@@ -607,0 +607,0 @@ |
@@ -38,3 +38,3 @@ import { Event, Expansion } from "../index"; | ||
export function lastTile(field: ResearchField) { | ||
export function lastTile(field: ResearchField): number { | ||
return researchTracks[field].length - 1; | ||
@@ -41,0 +41,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23632
1142144