Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 43 additions & 4 deletions src/render/BlockDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ type ModelVariantEntry = ModelVariant | (ModelVariant & {
})[]

type ModelMultiPartCondition = {
OR: ModelMultiPartCondition[],
OR?: ModelMultiPartCondition[],
AND?: ModelMultiPartCondition[],
} | {
[key: string]: string,
}
Expand All @@ -43,10 +44,10 @@ export class BlockDefinition {
const matches = Object.keys(this.variants).filter(v => this.matchesVariant(v, props))
if (matches.length === 0) return []
const variant = this.variants[matches[0]]
return [Array.isArray(variant) ? variant[0] : variant]
return [this.weightedApply(variant)]
} else if (this.multipart) {
const matches = this.multipart.filter(p => p.when ? this.matchesCase(p.when, props) : true)
return matches.map(p => Array.isArray(p.apply) ? p.apply[0] : p.apply)
return matches.map(p => this.weightedApply(p.apply))
}
return []
}
Expand Down Expand Up @@ -80,6 +81,41 @@ export class BlockDefinition {
return mesh.transform(t)
}

private weightedApply(apply: ModelVariantEntry) {
if (Array.isArray(apply)) {
// Sets the probability of the model for being used in the game,
// defaults to 1 (=100%). If more than one model is used for the same
// variant, the probability is calculated by dividing the individual
// model's weight by the sum of the weights of all models.
// (For example, if three models are used with weights 1, 1, and 2,
// then their combined weight would be 4 (1+1+2). The probability of each
// model being used would then be determined by dividing each weight
// by 4: 1/4, 1/4 and 2/4, or 25%, 25% and 50%, respectively.)

const totalWeight: number = apply
.reduce((sum, entry) => sum + (entry.weight ?? 1), 0);
let r: number = Math.random() * totalWeight;

// Iterate through the entries, subtracting weight until we find the selected one
for (const entry of apply) {
const w: number = entry.weight ?? 1;
if (r < w) {
// Destructure to drop the weight property and return the variant
const { weight, ...variant }: { weight?: number } & ModelVariant = entry;
return variant;
}
r -= w;
}

// Fallback (due to floating-point edge cases): return the last variant
const lastEntry = apply[apply.length - 1];
const { weight, ...variant }: { weight?: number } & ModelVariant = lastEntry;
return variant;
} else {
return apply
}
}

private matchesVariant(variant: string, props: { [key: string]: string }): boolean {
return variant.split(',').every(p => {
const [k, v] = p.split('=')
Expand All @@ -88,9 +124,12 @@ export class BlockDefinition {
}

private matchesCase(condition: ModelMultiPartCondition, props: { [key: string]: string }): boolean {
if (Array.isArray(condition.OR)) {
if (condition.OR && Array.isArray(condition.OR)) {
return condition.OR.some(c => this.matchesCase(c, props))
} else if (condition.AND && Array.isArray(condition.AND)) {
return condition.AND.every(c => this.matchesCase(c, props))
}

const states = condition as {[key: string]: string}
return Object.keys(states).every(k => {
const values = states[k].split('|')
Expand Down