-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathnpctools.js
118 lines (104 loc) · 3.39 KB
/
npctools.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// take in name of one npc, biome, and neighbours (list of names of npcs)
// return happiness of the one npc (# modifier) multiplied by the npc's weight
/**
* @param {keyof typeof npcdict} name
* @param {Biome[]} biome
* @param {(keyof typeof npcdict)[]} townNpcs
*/
function oneHappiness(name, biome, townNpcs) {
let happ = 1.0;
let npc = npcdict[name];
if (name === "Princess") {
if (townNpcs.length < 2) { return 1.5 * npc.weighting; }
happ *= Math.pow(0.88, Math.min(3, townNpcs.length));
return +Math.max(happ, 0.75).toFixed(2) * npc.weighting;
}
// crowdedness
// Since 1.4.3.3, crowded penalties start at 5 and solitude ends at 3
if (townNpcs.length >= 5) {
happ *= 1.05 ** (townNpcs.length - 3);
} else if (townNpcs.length <= 3) {
happ *= 0.95;
}
// right biome
if (biome.includes(npc.biome_loved)) {
happ *= 0.88;
} else if (biome.includes(npc.biome_liked)) {
happ *= 0.94;
}
// wrong biome
else if (biome.includes(npc.biome_disliked)) {
happ *= 1.06;
} else if (biome.includes(npc.biome_hated)) {
happ *= 1.12;
}
// neighbours
for (const n of townNpcs) {
if (name == n) {
continue;
}
if (npc.loves.includes(n)) {
happ *= 0.88;
} else if (npc.likes.includes(n) || n === "Princess") {
happ *= 0.94;
} else if (npc.dislikes.includes(n)) {
happ *= 1.06;
} else if (npc.hates.includes(n)) {
happ *= 1.12;
}
}
// enforce upper/lower bound on happiness
happ = Math.min(1.5, happ);
happ = Math.max(0.75, happ);
// round to 2dp
// weighting is how much we care about that npc
return +happ.toFixed(2) * npc.weighting;
}
/** @param {Iterable<(keyof typeof npcdict)>} group */
function sumOfWeights(group) {
let totalWeight = 0;
for (const person of group) {
totalWeight += npcdict[person].weighting;
}
return totalWeight;
}
// input group of names of npcs
// return array of the biome(s) which minimise happiness
/**
* @param {(keyof typeof npcdict)[]} group
* @returns {Biome[][]} Best biomes
*/
function bestBiomesForGroup(group) {
let lowestHappinessSoFar = Infinity;
let bestBiomesSoFar = [];
for (const biome of biomes) {
// Skip any biome configuration where the truffle is improperly housed
// Technically mods (like Fargo's) allow us to misplace the truffle
// but it doesn't really matter that much
if (!allowMisplacedTruffle && group.includes("Truffle") && (!biome.includes("Mushroom") || biome.includes("Caverns"))) { continue; }
let thisBiomeHappiness = 0.0;
for (const person of group) {
thisBiomeHappiness += oneHappiness(person, biome, group);
}
thisBiomeHappiness = +thisBiomeHappiness.toFixed(8);
if (thisBiomeHappiness < lowestHappinessSoFar) {
bestBiomesSoFar = [biome];
lowestHappinessSoFar = thisBiomeHappiness;
} else if (thisBiomeHappiness === lowestHappinessSoFar) {
bestBiomesSoFar.push(biome);
}
}
return bestBiomesSoFar;
}
/**
* @param {(keyof typeof npcdict)[]} group
* @returns {[number, number, Biome[][]]} Average happiness, total group weight, best biomes
*/
function groupHappWeightBiomes(group) {
let bestBiomes = bestBiomesForGroup(group);
let thisGroupHappiness = 0.0;
for (const person of group) {
thisGroupHappiness += oneHappiness(person, bestBiomes[0], group);
}
return [thisGroupHappiness / sumOfWeights(group), sumOfWeights(group), bestBiomes];
}