@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
580 lines (506 loc) • 23.5 kB
JavaScript
import Vector3 from "../../src/core/geom/Vector3js";
import { NumericInterval } from "../../src/core/math/interval/NumericInterval.js";
import { CellFilterCellMatcher } from "../../src/generation/filtering/CellFilterCellMatcher.js";
import { CellFilterCache } from "../../src/generation/filtering/numeric/CellFilterCache.js";
import { CellFilterLiteralFloat } from "../../src/generation/filtering/numeric/CellFilterLiteralFloat.js";
import { CellFilterAngleToNormal } from "../../src/generation/filtering/numeric/complex/CellFilterAngleToNormal.js";
import { CellFilterGaussianBlur } from "../../src/generation/filtering/numeric/complex/CellFilterGaussianBlur.js";
import { CellFilterSimplexNoise } from "../../src/generation/filtering/numeric/complex/CellFilterSimplexNoise.js";
import { CellFilterAdd } from "../../src/generation/filtering/numeric/math/algebra/CellFilterAdd.js";
import { CellFilterDivide } from "../../src/generation/filtering/numeric/math/algebra/CellFilterDivide.js";
import { CellFilterMultiply } from "../../src/generation/filtering/numeric/math/algebra/CellFilterMultiply.js";
import { CellFilterSubtract } from "../../src/generation/filtering/numeric/math/algebra/CellFilterSubtract.js";
import { CellFilterClamp } from "../../src/generation/filtering/numeric/math/CellFilterClamp.js";
import { CellFilterInverseLerp } from "../../src/generation/filtering/numeric/math/CellFilterInverseLerp.js";
import { CellFilterLerp } from "../../src/generation/filtering/numeric/math/CellFilterLerp.js";
import { CellFilterMax2 } from "../../src/generation/filtering/numeric/math/CellFilterMax2.js";
import { CellFilterOneMinus } from "../../src/generation/filtering/numeric/math/CellFilterOneMinus.js";
import { CellFilterSmoothStep } from "../../src/generation/filtering/numeric/math/CellFilterSmoothStep.js";
import { CellFilterStep } from "../../src/generation/filtering/numeric/math/CellFilterStep.js";
import { CellFilterCubicFunction } from "../../src/generation/filtering/numeric/math/poly/CellFilterCubicFunction.js";
import {
CellFilterSampleLayerLinear
} from "../../src/generation/filtering/numeric/sampling/CellFilterSampleLayerLinear.js";
import { GridTaskActionRuleSet } from "../../src/generation/grid/generation/discrete/GridTaskActionRuleSet.js";
import { GridTaskCellularAutomata } from "../../src/generation/grid/generation/discrete/GridTaskCellularAutomata.js";
import { GridTaskConnectRooms } from "../../src/generation/grid/generation/discrete/GridTaskConnectRooms.js";
import {
GridTaskBuildSourceDistanceMap
} from "../../src/generation/grid/generation/discrete/layer/GridTaskBuildSourceDistanceMap.js";
import {
GridTaskDensityMarkerDistribution
} from "../../src/generation/grid/generation/GridTaskDensityMarkerDistribution.js";
import { GridTaskSequence } from "../../src/generation/grid/generation/GridTaskSequence.js";
import { GridTaskGenerateRoads } from "../../src/generation/grid/generation/road/GridTaskGenerateRoads.js";
import { GridTaskGroup } from "../../src/generation/GridTaskGroup.js";
import { GridActionRuleSet } from "../../src/generation/markers/GridActionRuleSet.js";
import { GridCellActionPlaceMarker } from "../../src/generation/markers/GridCellActionPlaceMarker.js";
import { MarkerNodeMatcherByType } from "../../src/generation/markers/matcher/MarkerNodeMatcherByType.js";
import {
MarkerNodeTransformerRecordProperty
} from "../../src/generation/markers/transform/MarkerNodeTransformerRecordProperty.js";
import {
MarkerNodeTransformerYRotateByFilter
} from "../../src/generation/markers/transform/MarkerNodeTransformerYRotateByFilter.js";
import { GridCellActionPlaceTags } from "../../src/generation/placement/action/GridCellActionPlaceTags.js";
import {
GridCellActionWriteFilterToLayer
} from "../../src/generation/placement/action/GridCellActionWriteFilterToLayer.js";
import { GridCellActionSequence } from "../../src/generation/placement/action/util/GridCellActionSequence.js";
import { GridCellPlacementRule } from "../../src/generation/placement/GridCellPlacementRule.js";
import {
CellMatcherContainsMarkerWithinRadius
} from "../../src/generation/rules/cell/CellMatcherContainsMarkerWithinRadius.js";
import { CellMatcherGridPattern } from "../../src/generation/rules/cell/CellMatcherGridPattern.js";
import { GridPatternMatcherCell } from "../../src/generation/rules/cell/GridPatternMatcherCell.js";
import { CellMatcherAny } from "../../src/generation/rules/CellMatcherAny.js";
import { CellMatcherLayerBitMaskTest } from "../../src/generation/rules/CellMatcherLayerBitMaskTest.js";
import { CellMatcherAnd } from "../../src/generation/rules/logic/CellMatcherAnd.js";
import { CellMatcherNot } from "../../src/generation/rules/logic/CellMatcherNot.js";
import { SampleGroundMoistureFilter } from "./filters/SampleGroundMoistureFilter.js";
import { SampleNoise20_0 } from "./filters/SampleNoise20_0.js";
import { mir_generator_place_bases } from "./generators/mir_generator_place_bases.js";
import { mir_generator_place_road_decorators } from "./generators/mir_generator_place_road_decorators.js";
import { mir_generator_place_starting_point } from "./generators/mir_generator_place_starting_point.js";
import { GridTags } from "./grid/GridTags.js";
import { MirGridLayers } from "./grid/MirGridLayers.js";
import { matcher_not_play_area } from "./rules/matcher_not_play_area.js";
import { matcher_tag_traversable } from "./rules/matcher_tag_traversable.js";
import { matcher_tag_traversable_unoccupied } from "./rules/matcher_tag_traversable_unoccupied.js";
import { mir_matcher_attack_corridor } from "./rules/mir_matcher_attack_corridor.js";
export const SampleGenerator0 = new GridTaskGroup();
const pTreasureCorner = new CellMatcherGridPattern();
const MATCH_EMPTY = CellMatcherLayerBitMaskTest.from(GridTags.Traversable, MirGridLayers.Tags);
const MATCH_TREASURE = CellMatcherLayerBitMaskTest.from(GridTags.Treasure, MirGridLayers.Tags);
const MATCH_NOT_EMPTY = CellMatcherNot.from(MATCH_EMPTY);
const MATCH_STARTING_POINT = CellMatcherLayerBitMaskTest.from(GridTags.StartingPoint, MirGridLayers.Tags);
const MATCH_ENEMY = CellMatcherLayerBitMaskTest.from(GridTags.Enemy, MirGridLayers.Tags);
pTreasureCorner.addRule(1, 0, MATCH_NOT_EMPTY);
pTreasureCorner.addRule(0, 1, MATCH_NOT_EMPTY);
pTreasureCorner.addRule(0, 0, matcher_tag_traversable_unoccupied);
const pNoTreasureIn3 = new CellMatcherGridPattern();
pNoTreasureIn3.addRule(0, 0, CellMatcherNot.from(CellMatcherContainsMarkerWithinRadius.from(
MarkerNodeMatcherByType.from('Treasure'), 3
)));
const chestPlacementRule = GridCellPlacementRule.from({
matcher: CellMatcherAnd.from(pTreasureCorner, pNoTreasureIn3),
action: GridCellActionSequence.from([
GridCellActionPlaceMarker.from({ type: 'Treasure', size: 0.5 }),
GridCellActionPlaceTags.from(GridTags.Treasure, MirGridLayers.Tags)
]),
probability: CellFilterLiteralFloat.from(0.5)
});
const aMakePlayArea = GridCellActionSequence.from([
GridCellActionPlaceTags.from(GridTags.Traversable | GridTags.PlayArea, MirGridLayers.Tags)
]);
const gMakeEmpty = GridTaskCellularAutomata.from({
action: aMakePlayArea,
margin: 3
});
const gConnectRooms = GridTaskConnectRooms.from({
matcher: matcher_tag_traversable,
action: aMakePlayArea
});
gConnectRooms.addDependency(gMakeEmpty);
const gRuleSet1 = GridTaskActionRuleSet.from({ rules: GridActionRuleSet.from({ rules: [chestPlacementRule] }) });
const pNearTreasure = new CellMatcherGridPattern();
pNearTreasure.addRule(0, 0,
CellMatcherAnd.from(
matcher_tag_traversable_unoccupied,
CellMatcherNot.from(MATCH_ENEMY)
)
);
pNearTreasure.addRule(1, 1, MATCH_TREASURE);
pNearTreasure.addRule(2, 2, MATCH_NOT_EMPTY);
const MATCH_ENEMY_IN_3 = CellMatcherContainsMarkerWithinRadius.from(MarkerNodeMatcherByType.from('Enemy'), 3);
const MATCH_NO_ENEMY_IN_3 = CellMatcherNot.from(MATCH_ENEMY_IN_3);
const pNoEnemyIn3 = new CellMatcherGridPattern();
pNoEnemyIn3.addRule(0, 0, MATCH_NO_ENEMY_IN_3);
const ACTION_PLACE_ENEMY_MARKER = GridCellActionPlaceMarker.from({
type: 'Enemy',
size: 0.5,
transformers: [
MarkerNodeTransformerRecordProperty.from(
'power',
CellFilterAdd.from(
CellFilterMultiply.from(
CellFilterCubicFunction.from(
CellFilterMax2.from(
CellFilterSubtract.from(
CellFilterMultiply.from(
CellFilterDivide.from(
CellFilterSampleLayerLinear.from(MirGridLayers.DistanceFromStart),
//increase level by 1 for each X tiles distance
CellFilterLiteralFloat.from(20)
),
//add a bit of noise to difficulty distribution
CellFilterMultiply.from(
CellFilterSmoothStep.from(
CellFilterLiteralFloat.from(20),
CellFilterLiteralFloat.from(50),
CellFilterSampleLayerLinear.from(MirGridLayers.DistanceFromStart)
),
CellFilterLerp.from(
CellFilterLiteralFloat.from(0.9),
CellFilterLiteralFloat.from(2),
CellFilterMultiply.from(
CellFilterSimplexNoise.from(111.1134, 111.1134, 12319518),
CellFilterSimplexNoise.from(25.4827, 25.4827, 4512371)
)
)
)
),
CellFilterLiteralFloat.from(1)
),
CellFilterLiteralFloat.from(0)
),
0,
1, //linear factor
0.05, //quadratic factor (good for estimating branching)
0.01 //cubic factor
),
CellFilterLiteralFloat.from(100)
),
CellFilterLiteralFloat.from(50)
)
)
]
});
const ACTION_PLACE_ENEMY_TAG = GridCellActionPlaceTags.from(GridTags.Enemy | GridTags.Occupied, MirGridLayers.Tags);
const prTreasureGuards = GridCellPlacementRule.from(
{
matcher: pNearTreasure,
action: GridCellActionSequence.from([
ACTION_PLACE_ENEMY_MARKER,
ACTION_PLACE_ENEMY_TAG
])
}
);
const gRuleSetTreasureGuards = GridTaskActionRuleSet.from({ rules: GridActionRuleSet.from({ rules: [prTreasureGuards] }) });
gRuleSetTreasureGuards.addDependency(gRuleSet1);
const prEnemyTreasureGuard = GridCellPlacementRule.from(
{
matcher: CellMatcherAnd.from(
pNearTreasure,
pNoEnemyIn3
),
action: GridCellActionSequence.from([
ACTION_PLACE_ENEMY_MARKER,
ACTION_PLACE_ENEMY_TAG
])
}
);
const prEnemyCorridorGuard = GridCellPlacementRule.from(
{
matcher: CellMatcherAnd.from(
mir_matcher_attack_corridor,
pNoEnemyIn3
),
action: GridCellActionSequence.from([
ACTION_PLACE_ENEMY_MARKER,
ACTION_PLACE_ENEMY_TAG
]),
probability: CellFilterLiteralFloat.from(0.5)
}
);
const gRuleSet2 = GridTaskActionRuleSet.from({ rules: GridActionRuleSet.from({ rules: [prEnemyTreasureGuard, prEnemyCorridorGuard] }) });
gRuleSet2.addDependency(gRuleSetTreasureGuards);
// Place starting point tag
const gPlaceStartingPoint = mir_generator_place_starting_point();
gPlaceStartingPoint.addDependency(gConnectRooms);
const gBuildDistanceMap = GridTaskBuildSourceDistanceMap.from({
source: MATCH_STARTING_POINT,
pass: MATCH_EMPTY,
layer: MirGridLayers.DistanceFromStart
});
gBuildDistanceMap.addDependency(gPlaceStartingPoint);
gRuleSet1.addDependency(gBuildDistanceMap);
const gBases = mir_generator_place_bases();
gBases.addDependency(gMakeEmpty);
gConnectRooms.addDependency(gBases);
gPlaceStartingPoint.addDependency(gBases);
gRuleSet1.addDependency(gBases);
const gRoads = new GridTaskGenerateRoads();
gRoads.addDependency(gConnectRooms);
gRoads.addDependency(gBases);
const gRoadDecorators = mir_generator_place_road_decorators();
gRoadDecorators.addDependency(gRoads);
/**
*
* @type {GridTaskActionRuleSet}
*/
const gDrawLayerMoisture = GridTaskActionRuleSet.from({
rules: GridActionRuleSet.from({
rules: [
GridCellPlacementRule.from(
{
matcher: CellMatcherAny.INSTANCE,
action: GridCellActionWriteFilterToLayer.from(MirGridLayers.Moisture, SampleGroundMoistureFilter)
}
)
]
})
});
//trees
const fReadHeight = CellFilterSampleLayerLinear.from(MirGridLayers.Heights);
const fTreeArea = CellFilterCache.from(
CellFilterMultiply.from(
CellFilterMultiply.from(
SampleNoise20_0,
CellFilterClamp.from(
CellFilterInverseLerp.from(
CellFilterLiteralFloat.from(0.1),
CellFilterLiteralFloat.from(0.5),
CellFilterSampleLayerLinear.from(MirGridLayers.Moisture)
),
CellFilterLiteralFloat.from(0),
CellFilterLiteralFloat.from(1)
)
),
CellFilterMultiply.from(
CellFilterMultiply.from(
//filter out areas that are below height of 0
CellFilterStep.from(
CellFilterLiteralFloat.from(0),
fReadHeight
),
//filter areas that are playable
CellFilterCellMatcher.from(
matcher_not_play_area
)
),
// Filter areas with sharp slopes
CellFilterOneMinus.from(
CellFilterSmoothStep.from(
CellFilterLiteralFloat.from(Math.PI / 9),
CellFilterLiteralFloat.from(Math.PI / 5),
CellFilterAngleToNormal.from(fReadHeight, Vector3.forward)
)
)
)
)
);
const fFlatlandTrees = CellFilterCache.from(
CellFilterMultiply.from(
fTreeArea,
CellFilterClamp.from(
CellFilterInverseLerp.from(
CellFilterLiteralFloat.from(0.2),
CellFilterLiteralFloat.from(0),
CellFilterCache.from(
CellFilterGaussianBlur.from(
CellFilterAngleToNormal.from(fReadHeight),
2.3,
2.3
)
)
),
CellFilterLiteralFloat.from(0),
CellFilterLiteralFloat.from(1)
)
)
);
const matcher_non_play_area_3x3 = CellMatcherGridPattern.from([
GridPatternMatcherCell.from(matcher_not_play_area, -1, -1),
GridPatternMatcherCell.from(matcher_not_play_area, 0, -1),
GridPatternMatcherCell.from(matcher_not_play_area, 1, -1),
GridPatternMatcherCell.from(matcher_not_play_area, -1, 0),
GridPatternMatcherCell.from(matcher_not_play_area, 0, 0),
GridPatternMatcherCell.from(matcher_not_play_area, 1, 0),
GridPatternMatcherCell.from(matcher_not_play_area, -1, 1),
GridPatternMatcherCell.from(matcher_not_play_area, 0, 1),
GridPatternMatcherCell.from(matcher_not_play_area, 1, 1),
]);
const filterNonPlayableArea_3x3 = CellFilterCellMatcher.from(matcher_non_play_area_3x3);
const gFoliageLarge = GridTaskSequence.from([
GridTaskDensityMarkerDistribution.from(
CellFilterCache.from(
CellFilterMultiply.from(
CellFilterMultiply.from(
fFlatlandTrees,
//trees take up quite a bit of space, make sure they are far enough from play area
filterNonPlayableArea_3x3
),
CellFilterLiteralFloat.from(10)
)
),
GridCellActionPlaceMarker.from({
type: 'Tree-Flatland-Large',
size: 0.5,
transformers: [
MarkerNodeTransformerYRotateByFilter.from(
CellFilterMultiply.from(
CellFilterSimplexNoise.from(
3.1234, 3.1234, 90127151
),
CellFilterLiteralFloat.from(123421)
)
)
]
}),
new NumericInterval(2.7, 3.4)
),
GridTaskDensityMarkerDistribution.from(
CellFilterMultiply.from(
fFlatlandTrees,
CellFilterLiteralFloat.from(20)
),
GridCellActionPlaceMarker.from({
type: 'Tree-Flatland-Small',
size: 0.5,
transformers: [
MarkerNodeTransformerYRotateByFilter.from(
CellFilterMultiply.from(
CellFilterSimplexNoise.from(
3.1234, 3.1234, 90127151
),
CellFilterLiteralFloat.from(123421)
)
)
]
}),
new NumericInterval(1.7, 2)
)
]);
const fSharpSlope = CellFilterCache.from(
CellFilterSmoothStep.from(
CellFilterLiteralFloat.from(Math.PI / 5),
CellFilterLiteralFloat.from(Math.PI / 3.5),
CellFilterAngleToNormal.from(
CellFilterSampleLayerLinear.from(MirGridLayers.Heights),
Vector3.forward
)
)
);
const filterMushroom = fTreeArea;
const gFoliageSmall = GridTaskSequence.from([
GridTaskDensityMarkerDistribution.from(
CellFilterMultiply.from(
filterMushroom,
CellFilterLiteralFloat.from(0.02)
),
GridCellActionPlaceMarker.from({
type: 'Mushroom-1',
size: 0.5,
transformers: []
}),
new NumericInterval(0.3, 0.37),
42
),
GridTaskDensityMarkerDistribution.from(
CellFilterMultiply.from(
filterMushroom,
CellFilterLiteralFloat.from(0.05)
),
GridCellActionPlaceMarker.from({
type: 'Mushroom-0',
size: 0.5,
transformers: []
}),
new NumericInterval(0.17, 0.25),
9000234
),
GridTaskDensityMarkerDistribution.from(
CellFilterCache.from(
CellFilterMultiply.from(
CellFilterMultiply.from(
CellFilterSubtract.from(
CellFilterGaussianBlur.from(
fSharpSlope,
3,
3
),
fSharpSlope
),
CellFilterCellMatcher.from(matcher_not_play_area)
),
CellFilterLiteralFloat.from(0.15)
)
),
GridCellActionPlaceMarker.from({
type: 'Stone-0',
size: 0.5,
transformers: []
}),
new NumericInterval(0.15, 0.35),
9000234
)
]);
//heights
const mHeightArea = new CellMatcherGridPattern();
mHeightArea.addRule(0, -2, matcher_not_play_area);
mHeightArea.addRule(-1, -1, matcher_not_play_area);
mHeightArea.addRule(0, -1, matcher_not_play_area);
mHeightArea.addRule(1, -1, matcher_not_play_area);
mHeightArea.addRule(-2, 0, matcher_not_play_area);
mHeightArea.addRule(-1, 0, matcher_not_play_area);
mHeightArea.addRule(0, 0, matcher_not_play_area);
mHeightArea.addRule(1, 0, matcher_not_play_area);
mHeightArea.addRule(2, 0, matcher_not_play_area);
mHeightArea.addRule(-1, 1, matcher_not_play_area);
mHeightArea.addRule(0, 1, matcher_not_play_area);
mHeightArea.addRule(1, 1, matcher_not_play_area);
mHeightArea.addRule(0, 2, matcher_not_play_area);
const gHeights = GridTaskActionRuleSet.from({
rules: GridActionRuleSet.from(
{
rules: [
GridCellPlacementRule.from(
{
matcher: CellMatcherAny.INSTANCE,
actions: GridCellActionWriteFilterToLayer.from(
MirGridLayers.Heights,
CellFilterLerp.from(
CellFilterLiteralFloat.from(0),
CellFilterLerp.from(
CellFilterLiteralFloat.from(-2),
CellFilterLiteralFloat.from(7),
CellFilterMultiply.from(
CellFilterSimplexNoise.from(30, 30),
CellFilterSimplexNoise.from(13, 13)
)
),
CellFilterGaussianBlur.from(
CellFilterCache.from(
CellFilterCellMatcher.from(
mHeightArea
)
),
1.5,
1.5
)
)
)
}
)
]
}
), resolution: 1
});
gHeights.addDependency(gConnectRooms);
gFoliageLarge.addDependencies([
gMakeEmpty,
gConnectRooms,
gHeights,
gDrawLayerMoisture,
gRuleSet1,
gRuleSet2,
gRuleSetTreasureGuards,
gBases
]);
gFoliageSmall.addDependencies([gFoliageLarge]);
gDrawLayerMoisture.addDependencies([gHeights]);
SampleGenerator0.addGenerator(gHeights);
SampleGenerator0.addGenerator(gDrawLayerMoisture);
SampleGenerator0.addGenerator(gMakeEmpty);
SampleGenerator0.addGenerator(gConnectRooms);
SampleGenerator0.addGenerator(gRoads);
SampleGenerator0.addGenerator(gRoadDecorators);
SampleGenerator0.addGenerator(gBases);
SampleGenerator0.addGenerator(gPlaceStartingPoint);
SampleGenerator0.addGenerator(gBuildDistanceMap);
SampleGenerator0.addGenerator(gRuleSet1);
SampleGenerator0.addGenerator(gRuleSet2);
SampleGenerator0.addGenerator(gRuleSetTreasureGuards);
SampleGenerator0.addGenerator(gFoliageLarge);
SampleGenerator0.addGenerator(gFoliageSmall);