-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add rarity for npc pickpocketing (#571)
- Loading branch information
Showing
12 changed files
with
333 additions
and
160 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
107 changes: 107 additions & 0 deletions
107
src/main/java/dinkplugin/util/AbstractRarityService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package dinkplugin.util; | ||
|
||
import com.google.gson.Gson; | ||
import com.google.gson.annotations.SerializedName; | ||
import com.google.gson.reflect.TypeToken; | ||
import lombok.AccessLevel; | ||
import lombok.Data; | ||
import lombok.Setter; | ||
import lombok.Value; | ||
import lombok.extern.slf4j.Slf4j; | ||
import net.runelite.api.ItemComposition; | ||
import net.runelite.client.game.ItemManager; | ||
import net.runelite.client.game.ItemVariationMapping; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.InputStream; | ||
import java.io.InputStreamReader; | ||
import java.io.Reader; | ||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
|
||
@Slf4j | ||
public abstract class AbstractRarityService { | ||
|
||
protected final Gson gson; | ||
protected final ItemManager itemManager; | ||
protected final Map<String, Collection<RareDrop>> dropsBySourceName; | ||
|
||
AbstractRarityService(String resourceName, int expectedSize, Gson gson, ItemManager itemManager) { | ||
this.gson = gson; | ||
this.itemManager = itemManager; | ||
this.dropsBySourceName = new HashMap<>(expectedSize); | ||
|
||
Map<String, List<RawDrop>> raw; | ||
try (InputStream is = getClass().getResourceAsStream(resourceName); | ||
Reader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) { | ||
raw = gson.fromJson(reader, new TypeToken<Map<String, List<RawDrop>>>() {}.getType()); | ||
} catch (Exception e) { | ||
log.error("Failed to read monster drop rates", e); | ||
return; | ||
} | ||
|
||
raw.forEach((sourceName, rawDrops) -> { | ||
ArrayList<RareDrop> drops = rawDrops.stream() | ||
.map(RawDrop::transform) | ||
.flatMap(Collection::stream) | ||
.collect(Collectors.toCollection(ArrayList::new)); | ||
drops.trimToSize(); | ||
dropsBySourceName.put(sourceName, drops); | ||
}); | ||
} | ||
|
||
public OptionalDouble getRarity(String sourceName, int itemId, int quantity) { | ||
ItemComposition composition = itemId >= 0 ? itemManager.getItemComposition(itemId) : null; | ||
int canonical = composition != null && composition.getNote() != -1 ? composition.getLinkedNoteId() : itemId; | ||
String itemName = composition != null ? composition.getMembersName() : ""; | ||
Collection<Integer> variants = new HashSet<>( | ||
ItemVariationMapping.getVariations(ItemVariationMapping.map(canonical)) | ||
); | ||
return dropsBySourceName.getOrDefault(sourceName, Collections.emptyList()) | ||
.stream() | ||
.filter(drop -> drop.getMinQuantity() <= quantity && quantity <= drop.getMaxQuantity()) | ||
.filter(drop -> { | ||
int id = drop.getItemId(); | ||
if (id == itemId) return true; | ||
return variants.contains(id) && itemName.equals(itemManager.getItemComposition(id).getMembersName()); | ||
}) | ||
.mapToDouble(RareDrop::getProbability) | ||
.reduce(Double::sum); | ||
} | ||
|
||
@Value | ||
protected static class RareDrop { | ||
int itemId; | ||
int minQuantity; | ||
int maxQuantity; | ||
double probability; | ||
} | ||
|
||
@Data | ||
@Setter(AccessLevel.PRIVATE) | ||
private static class RawDrop { | ||
private @SerializedName("i") int itemId; | ||
private @SerializedName("r") Integer rolls; | ||
private @SerializedName("d") double denominator; | ||
private @SerializedName("q") Integer quantity; | ||
private @SerializedName("m") Integer quantMin; | ||
private @SerializedName("n") Integer quantMax; | ||
|
||
Collection<RareDrop> transform() { | ||
int rounds = rolls != null ? rolls : 1; | ||
int min = quantMin != null ? quantMin : quantity; | ||
int max = quantMax != null ? quantMax : quantity; | ||
double prob = 1 / denominator; | ||
|
||
if (rounds == 1) { | ||
return List.of(new RareDrop(itemId, min, max, prob)); | ||
} | ||
List<RareDrop> drops = new ArrayList<>(rounds); | ||
for (int successCount = 1; successCount <= rounds; successCount++) { | ||
double density = MathUtils.binomialProbability(prob, rounds, successCount); | ||
drops.add(new RareDrop(itemId, min * successCount, max * successCount, density)); | ||
} | ||
return drops; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,114 +1,15 @@ | ||
package dinkplugin.util; | ||
|
||
import com.google.gson.Gson; | ||
import com.google.gson.annotations.SerializedName; | ||
import com.google.gson.reflect.TypeToken; | ||
import lombok.AccessLevel; | ||
import lombok.Data; | ||
import lombok.Setter; | ||
import lombok.Value; | ||
import lombok.extern.slf4j.Slf4j; | ||
import net.runelite.api.ItemComposition; | ||
import net.runelite.client.game.ItemManager; | ||
import net.runelite.client.game.ItemVariationMapping; | ||
|
||
import javax.inject.Inject; | ||
import javax.inject.Singleton; | ||
import java.io.BufferedReader; | ||
import java.io.InputStream; | ||
import java.io.InputStreamReader; | ||
import java.io.Reader; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.OptionalDouble; | ||
import java.util.stream.Collectors; | ||
|
||
@Slf4j | ||
@Singleton | ||
public class RarityService { | ||
private final Map<String, Collection<Drop>> dropsByNpcName = new HashMap<>(1024); | ||
private @Inject Gson gson; | ||
private @Inject ItemManager itemManager; | ||
|
||
public class RarityService extends AbstractRarityService { | ||
@Inject | ||
void init() { | ||
Map<String, List<RawDrop>> raw; | ||
try (InputStream is = getClass().getResourceAsStream("/npc_drops.json"); | ||
Reader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) { | ||
raw = gson.fromJson(reader, | ||
new TypeToken<Map<String, List<RawDrop>>>() {}.getType()); | ||
} catch (Exception e) { | ||
log.error("Failed to read monster drop rates", e); | ||
return; | ||
} | ||
|
||
raw.forEach((npcName, rawDrops) -> { | ||
List<Drop> drops = rawDrops.stream() | ||
.map(RawDrop::transform) | ||
.flatMap(Collection::stream) | ||
.collect(Collectors.toList()); | ||
dropsByNpcName.put(npcName, drops); | ||
}); | ||
} | ||
|
||
public OptionalDouble getRarity(String npcName, int itemId, int quantity) { | ||
ItemComposition composition = itemId >= 0 ? itemManager.getItemComposition(itemId) : null; | ||
int canonical = composition != null && composition.getNote() != -1 ? composition.getLinkedNoteId() : itemId; | ||
String itemName = composition != null ? composition.getMembersName() : ""; | ||
Collection<Integer> variants = new HashSet<>( | ||
ItemVariationMapping.getVariations(ItemVariationMapping.map(canonical)) | ||
); | ||
return dropsByNpcName.getOrDefault(npcName, Collections.emptyList()) | ||
.stream() | ||
.filter(drop -> drop.getMinQuantity() <= quantity && quantity <= drop.getMaxQuantity()) | ||
.filter(drop -> { | ||
int id = drop.getItemId(); | ||
if (id == itemId) return true; | ||
return variants.contains(id) && itemName.equals(itemManager.getItemComposition(id).getMembersName()); | ||
}) | ||
.mapToDouble(Drop::getProbability) | ||
.reduce(Double::sum); | ||
} | ||
|
||
@Value | ||
private static class Drop { | ||
int itemId; | ||
int minQuantity; | ||
int maxQuantity; | ||
double probability; | ||
} | ||
|
||
@Data | ||
@Setter(AccessLevel.PRIVATE) | ||
private static class RawDrop { | ||
private @SerializedName("i") int itemId; | ||
private @SerializedName("r") Integer rolls; | ||
private @SerializedName("d") double denominator; | ||
private @SerializedName("q") Integer quantity; | ||
private @SerializedName("m") Integer quantMin; | ||
private @SerializedName("n") Integer quantMax; | ||
|
||
Collection<Drop> transform() { | ||
int rounds = rolls != null ? rolls : 1; | ||
int min = quantMin != null ? quantMin : quantity; | ||
int max = quantMax != null ? quantMax : quantity; | ||
double prob = 1 / denominator; | ||
|
||
if (rounds == 1) { | ||
return List.of(new Drop(itemId, min, max, prob)); | ||
} | ||
List<Drop> drops = new ArrayList<>(rounds); | ||
for (int successCount = 1; successCount <= rounds; successCount++) { | ||
double density = MathUtils.binomialProbability(prob, rounds, successCount); | ||
drops.add(new Drop(itemId, min * successCount, max * successCount, density)); | ||
} | ||
return drops; | ||
} | ||
RarityService(Gson gson, ItemManager itemManager) { | ||
super("/npc_drops.json", 1024, gson, itemManager); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package dinkplugin.util; | ||
|
||
import com.google.gson.Gson; | ||
import net.runelite.api.ItemID; | ||
import net.runelite.client.game.ItemManager; | ||
|
||
import javax.inject.Inject; | ||
import javax.inject.Singleton; | ||
import java.util.OptionalDouble; | ||
|
||
@Singleton | ||
public class ThievingService extends AbstractRarityService { | ||
|
||
@Inject | ||
ThievingService(Gson gson, ItemManager itemManager) { | ||
super("/thieving.json", 32, gson, itemManager); | ||
} | ||
|
||
@Override | ||
public OptionalDouble getRarity(String sourceName, int itemId, int quantity) { | ||
if (itemId == ItemID.BLOOD_SHARD) { | ||
// https://oldschool.runescape.wiki/w/Blood_shard#Item_sources | ||
return OptionalDouble.of(1.0 / 5000); | ||
} | ||
|
||
if (itemId == ItemID.ENHANCED_CRYSTAL_TELEPORT_SEED) { | ||
// https://oldschool.runescape.wiki/w/Enhanced_crystal_teleport_seed#Item_sources | ||
return OptionalDouble.of(1.0 / 1024); | ||
} | ||
|
||
return super.getRarity(sourceName, itemId, quantity); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"Guard":[{"i":2809,"d":128,"q":1},{"i":22879,"d":896,"q":2}],"Gnome":[{"i":2809,"d":150,"q":1}],"H.A.M. Member":[{"i":4298,"d":100,"q":1},{"i":4300,"d":100,"q":1},{"i":4302,"d":100,"q":1},{"i":4304,"d":100,"q":1},{"i":4306,"d":100,"q":1},{"i":4308,"d":100,"q":1},{"i":4310,"d":100,"q":1}],"Hero":[{"i":12157,"d":1400,"q":1}],"Master Farmer":[{"i":5295,"d":302,"q":1},{"i":5296,"d":443,"q":1},{"i":5298,"d":947,"q":1},{"i":5299,"d":1389,"q":1},{"i":5300,"d":2083,"q":1},{"i":5301,"d":2976,"q":1},{"i":5303,"d":6944,"q":1},{"i":5304,"d":10417,"q":1},{"i":22879,"d":260,"q":1}],"Paladin":[{"i":3560,"d":1000,"q":1}],"TzHaar-Hur":[{"i":1617,"d":195,"q":1}],"Wealthy citizen":[{"i":2711,"d":85,"q":1}]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.