mirror of
https://github.com/collinsmith/riiablo.git
synced 2025-03-04 22:50:36 +07:00
Second iteration improvement for #24
Added support for stat aggregation Added support for stat value adding Organized property lists more appropriately with well-defined indexes Disabled set item stats until support is added for only using the appropriate property list
This commit is contained in:
parent
0d1fe7905c
commit
71a00195ea
@ -9,8 +9,6 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Disposable;
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException;
|
||||
import com.badlogic.gdx.utils.IntMap;
|
||||
import com.badlogic.gdx.utils.IntSet;
|
||||
import com.badlogic.gdx.utils.ObjectMap;
|
||||
import com.riiablo.CharacterClass;
|
||||
import com.riiablo.Riiablo;
|
||||
@ -66,7 +64,19 @@ public class Item extends Actor implements Disposable {
|
||||
private static final int INSCRIBED = 0x01000000;
|
||||
private static final int RUNEWORD = 0x04000000;
|
||||
|
||||
private static final PropertyList[] EMPTY_STAT_ARRAY = new PropertyList[0];
|
||||
private static final int MAGIC_PROPS = 0;
|
||||
private static final int SET_PROPS = 1;
|
||||
private static final int RUNE_PROPS = 6;
|
||||
private static final int NUM_PROPS = 7;
|
||||
private static final int MAGIC_PROPS_FLAG = 1 << MAGIC_PROPS;
|
||||
private static final int SET_2_PROPS_FLAG = 1 << SET_PROPS + 0;
|
||||
private static final int SET_3_PROPS_FLAG = 1 << SET_PROPS + 1;
|
||||
private static final int SET_4_PROPS_FLAG = 1 << SET_PROPS + 2;
|
||||
private static final int SET_5_PROPS_FLAG = 1 << SET_PROPS + 3;
|
||||
private static final int SET_6_PROPS_FLAG = 1 << SET_PROPS + 4;
|
||||
private static final int RUNE_PROPS_FLAG = 1 << RUNE_PROPS;
|
||||
|
||||
private static final PropertyList[] EMPTY_STAT_ARRAY = new PropertyList[NUM_PROPS];
|
||||
|
||||
private static final ObjectMap<String, String> WEAPON_DESC = new ObjectMap<>();
|
||||
static {
|
||||
@ -205,7 +215,7 @@ public class Item extends Actor implements Disposable {
|
||||
quality = Quality.valueOf(bitStream.readUnsigned7OrLess(4));
|
||||
pictureId = bitStream.readBoolean() ? bitStream.readUnsigned7OrLess(3) : -1;
|
||||
classOnly = bitStream.readBoolean() ? bitStream.readUnsigned15OrLess(11) : -1;
|
||||
int listsFlags = 1 << 0;
|
||||
int listsFlags = MAGIC_PROPS_FLAG;
|
||||
switch (quality) {
|
||||
case LOW:
|
||||
case HIGH:
|
||||
@ -240,7 +250,7 @@ public class Item extends Actor implements Disposable {
|
||||
|
||||
if ((flags & RUNEWORD) == RUNEWORD) {
|
||||
runewordData = bitStream.read16BitsOrLess(Short.SIZE);
|
||||
listsFlags |= (1 << 6);
|
||||
listsFlags |= RUNE_PROPS_FLAG;
|
||||
}
|
||||
|
||||
if ((flags & INSCRIBED) == INSCRIBED) {
|
||||
@ -275,15 +285,15 @@ public class Item extends Actor implements Disposable {
|
||||
|
||||
if (quality == SET) {
|
||||
int lists = bitStream.readUnsigned7OrLess(5);
|
||||
listsFlags |= (lists << 1);
|
||||
listsFlags |= (lists << SET_PROPS);
|
||||
}
|
||||
|
||||
if (type.is("book")) {
|
||||
listsFlags = 0;
|
||||
}
|
||||
|
||||
stats = new PropertyList[7];
|
||||
for (int i = 0; i < 7; i++) {
|
||||
stats = new PropertyList[NUM_PROPS];
|
||||
for (int i = 0; i < NUM_PROPS; i++) {
|
||||
if (((listsFlags >> i) & 1) == 1) {
|
||||
stats[i] = new PropertyList().read(bitStream);
|
||||
}
|
||||
@ -879,81 +889,37 @@ public class Item extends Actor implements Disposable {
|
||||
if (Item.this.type.is("weap")) {
|
||||
add(new Label(Riiablo.string.lookup(WEAPON_DESC.get(Item.this.base.type)) + " - " + 0, font, Riiablo.colors.white)).center().space(SPACING).row();
|
||||
}
|
||||
|
||||
//for (Stat.Instance stat : stats.values()) {
|
||||
// add(new Label(stat.stat.toString(), font, Riiablo.colors.white)).center().space(SPACING).row();
|
||||
//}
|
||||
}
|
||||
|
||||
// TODO: Detect stats with encoded groupings and auto join them into a grouped stat
|
||||
|
||||
for (int i = 0; i < stats.length; i++) {
|
||||
PropertyList props = Item.this.stats[i];
|
||||
if (props == null) continue;
|
||||
Array<Stat.Instance> propsArray = props.toArray();
|
||||
|
||||
// TODO: This can be cleaned up later
|
||||
IntMap<Array<Stat.Instance>> groups = new IntMap<>();
|
||||
for (Stat.Instance stat : propsArray) {
|
||||
int dgrp = stat.entry.dgrp;
|
||||
if (dgrp > 0) {
|
||||
Array<Stat.Instance> group = groups.get(dgrp);
|
||||
if (group == null) groups.put(dgrp, group = new Array<>());
|
||||
group.add(stat);
|
||||
}
|
||||
// magic props
|
||||
PropertyList magicProps = stats[MAGIC_PROPS];
|
||||
PropertyList runeProps = stats[RUNE_PROPS];
|
||||
if (magicProps != null) {
|
||||
PropertyList magicPropsAggregate = magicProps.copy();
|
||||
if (runeProps != null) magicPropsAggregate.addAll(runeProps);
|
||||
magicPropsAggregate.reduce();
|
||||
System.out.println(Item.this.getName());
|
||||
for (Stat.Instance stat : magicPropsAggregate.props.values()) {
|
||||
System.out.println(stat);
|
||||
}
|
||||
|
||||
IntSet groupReplaced = new IntSet();
|
||||
IntMap<Stat.Instance> groupReplacements = new IntMap<>();
|
||||
for (IntMap.Entry<Array<Stat.Instance>> group : groups) {
|
||||
switch (group.key) {
|
||||
case 1:
|
||||
case 2:
|
||||
if (group.value.size == 4) {
|
||||
boolean allEqual = true;
|
||||
Stat.Instance first = group.value.first();
|
||||
for (int j = 1; allEqual && j < group.value.size; j++) {
|
||||
Stat.Instance stat = group.value.get(j);
|
||||
allEqual = (stat.value == first.value) && (stat.param == first.param);
|
||||
}
|
||||
|
||||
if (allEqual) {
|
||||
groupReplacements.put(group.key, first);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
propsArray.sort(new Comparator<Stat.Instance>() {
|
||||
Array<Stat.Instance> aggregate = magicPropsAggregate.toArray();
|
||||
aggregate.sort(new Comparator<Stat.Instance>() {
|
||||
@Override
|
||||
public int compare(Stat.Instance o1, Stat.Instance o2) {
|
||||
return o2.entry.descpriority - o1.entry.descpriority;
|
||||
}
|
||||
});
|
||||
|
||||
for (Stat.Instance stat : propsArray) {
|
||||
Label label;
|
||||
int dgrp = stat.entry.dgrp;
|
||||
boolean group = false;
|
||||
if (dgrp > 0) {
|
||||
if (groupReplaced.contains(dgrp)) continue;
|
||||
Stat.Instance grp = groupReplacements.get(dgrp);
|
||||
if (grp != null) {
|
||||
stat = grp;
|
||||
group = true;
|
||||
groupReplaced.add(dgrp);
|
||||
}
|
||||
}
|
||||
|
||||
String text = stat.format(group);
|
||||
for (Stat.Instance stat : aggregate) {
|
||||
String text = stat.format(false);
|
||||
if (text == null) continue;
|
||||
label = new Label(text, font, Riiablo.colors.blue); // Conditionally, set should be green
|
||||
add(label).center().space(SPACING).row();
|
||||
add(new Label(text, font, Riiablo.colors.blue)).center().space(SPACING).row();
|
||||
}
|
||||
}
|
||||
|
||||
//PropertyList setProps = stats[SET_PROPS + 0]; // TODO: + num equipped set items
|
||||
// TODO: add set property support
|
||||
|
||||
StringBuilder itemFlags = null;
|
||||
if ((Item.this.flags & ETHEREAL) == ETHEREAL) {
|
||||
itemFlags = new StringBuilder(32);
|
||||
|
@ -5,10 +5,28 @@ import com.badlogic.gdx.utils.IntMap;
|
||||
import com.riiablo.codec.util.BitStream;
|
||||
|
||||
public class PropertyList {
|
||||
private static final int[] ATTRIBUTES = {Stat.strength, Stat.energy, Stat.dexterity, Stat.vitality};
|
||||
private static final int[] RESISTS = {Stat.fireresist, Stat.lightresist, Stat.coldresist, Stat.poisonresist};
|
||||
private static final int[] FIREDMG = {Stat.firemindam, Stat.firemaxdam};
|
||||
private static final int[] LIGHTDMG = {Stat.lightmindam, Stat.lightmaxdam};
|
||||
private static final int[] MAGICDMG = {Stat.magicmindam, Stat.magicmaxdam};
|
||||
private static final int[] COLDDMG = {Stat.coldmindam, Stat.coldmaxdam};
|
||||
private static final int[] POISONDMG = {Stat.poisonmindam, Stat.poisonmaxdam, Stat.poisonlength};
|
||||
private static final int[] ENHANCEDDMG = {Stat.item_mindamage_percent, Stat.item_maxdamage_percent};
|
||||
private static final int[] MINDMG = {Stat.mindamage, Stat.maxdamage};
|
||||
|
||||
final IntMap<Stat.Instance> props = new IntMap<>();
|
||||
|
||||
PropertyList() {}
|
||||
|
||||
PropertyList(PropertyList src) {
|
||||
props.putAll(src.props);
|
||||
}
|
||||
|
||||
public PropertyList copy() {
|
||||
return new PropertyList(this);
|
||||
}
|
||||
|
||||
public void put(int stat, int value) {
|
||||
props.put(stat, Stat.create(stat, value));
|
||||
}
|
||||
@ -36,4 +54,100 @@ public class PropertyList {
|
||||
public Array<Stat.Instance> toArray() {
|
||||
return props.values().toArray();
|
||||
}
|
||||
|
||||
public PropertyList addAll(PropertyList other) {
|
||||
for (IntMap.Entry<Stat.Instance> entry : other.props) {
|
||||
Stat.Instance existing = props.get(entry.key);
|
||||
if (existing != null) {
|
||||
existing.add(entry.value);
|
||||
} else {
|
||||
props.put(entry.key, entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public PropertyList reduce() {
|
||||
if (containsAll(ATTRIBUTES) && allEqual(ATTRIBUTES)) {
|
||||
int value = props.get(ATTRIBUTES[0]).value;
|
||||
for (int attr : ATTRIBUTES) props.remove(attr);
|
||||
put(Stat.all_attributes, value);
|
||||
}
|
||||
|
||||
if (containsAll(RESISTS) && allEqual(RESISTS)) {
|
||||
int value = props.get(RESISTS[0]).value;
|
||||
for (int attr : RESISTS) props.remove(attr);
|
||||
put(Stat.all_resistances, value);
|
||||
}
|
||||
|
||||
if (containsAll(ENHANCEDDMG) && allEqual(ENHANCEDDMG)) {
|
||||
int value = props.get(ENHANCEDDMG[0]).value;
|
||||
for (int attr : ENHANCEDDMG) props.remove(attr);
|
||||
put(Stat.enhanceddam, value);
|
||||
}
|
||||
|
||||
if (containsAll(MINDMG)) {
|
||||
Stat.Instance mindamage = get(Stat.mindamage);
|
||||
Stat.Instance maxdamage = get(Stat.maxdamage);
|
||||
for (int attr : MINDMG) props.remove(attr);
|
||||
props.put(Stat.mindam, new Stat.Aggregate(Stat.mindam, "strModMinDamage", "strModMinDamageRange", mindamage, maxdamage));
|
||||
}
|
||||
|
||||
if (containsAll(FIREDMG)) {
|
||||
Stat.Instance firemindam = get(Stat.firemindam);
|
||||
Stat.Instance firemaxdam = get(Stat.firemaxdam);
|
||||
for (int attr : FIREDMG) props.remove(attr);
|
||||
props.put(Stat.firedam, new Stat.Aggregate(Stat.firedam, "strModFireDamage", "strModFireDamageRange", firemindam, firemaxdam));
|
||||
}
|
||||
|
||||
if (containsAll(LIGHTDMG)) {
|
||||
Stat.Instance lightmindam = get(Stat.lightmindam);
|
||||
Stat.Instance lightmaxdam = get(Stat.lightmaxdam);
|
||||
for (int attr : LIGHTDMG) props.remove(attr);
|
||||
props.put(Stat.lightdam, new Stat.Aggregate(Stat.lightdam, "strModLightningDamage", "strModLightningDamageRange", lightmindam, lightmaxdam));
|
||||
}
|
||||
|
||||
if (containsAll(MAGICDMG)) {
|
||||
Stat.Instance magicmindam = get(Stat.magicmindam);
|
||||
Stat.Instance magicmaxdam = get(Stat.magicmaxdam);
|
||||
for (int attr : MAGICDMG) props.remove(attr);
|
||||
props.put(Stat.magicdam, new Stat.Aggregate(Stat.magicdam, "strModMagicDamage", "strModMagicDamageRange", magicmindam, magicmaxdam));
|
||||
}
|
||||
|
||||
if (containsAll(COLDDMG)) {
|
||||
Stat.Instance coldmindam = get(Stat.coldmindam);
|
||||
Stat.Instance coldmaxdam = get(Stat.coldmaxdam);
|
||||
for (int attr : COLDDMG) props.remove(attr);
|
||||
props.put(Stat.colddam, new Stat.Aggregate(Stat.colddam, "strModColdDamage", "strModColdDamageRange", coldmindam, coldmaxdam));
|
||||
}
|
||||
|
||||
if (containsAll(POISONDMG)) {
|
||||
Stat.Instance poisonmindam = get(Stat.poisonmindam);
|
||||
Stat.Instance poisonmaxdam = get(Stat.poisonmaxdam);
|
||||
Stat.Instance poisonlength = get(Stat.poisonlength);
|
||||
for (int attr : POISONDMG) props.remove(attr);
|
||||
props.put(Stat.poisondam, new Stat.Aggregate(Stat.poisondam, "strModPoisonDamage", "strModPoisonDamageRange", poisonmindam, poisonmaxdam, poisonlength));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean containsAll(int[] keys) {
|
||||
boolean result = true;
|
||||
for (int i = 0; result && i < keys.length; i++) {
|
||||
result = props.containsKey(keys[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean allEqual(int[] keys) {
|
||||
int value = props.get(keys[0]).value;
|
||||
for (int i = 1; i < keys.length; i++) {
|
||||
if (value != props.get(keys[i]).value) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import com.riiablo.codec.excel.SkillDesc;
|
||||
import com.riiablo.codec.excel.Skills;
|
||||
import com.riiablo.codec.util.BitStream;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class Stat {
|
||||
public static final int strength = 0;
|
||||
@ -376,6 +378,15 @@ public class Stat {
|
||||
// These don't actually exist in the game
|
||||
public static final int reqstr = NONE - 1;
|
||||
public static final int reqdex = NONE - 2;
|
||||
public static final int all_attributes = NONE - 3;
|
||||
public static final int all_resistances = NONE - 4;
|
||||
public static final int mindam = NONE - 5;
|
||||
public static final int enhanceddam = NONE - 6;
|
||||
public static final int firedam = NONE - 7;
|
||||
public static final int lightdam = NONE - 8;
|
||||
public static final int magicdam = NONE - 9;
|
||||
public static final int colddam = NONE - 10;
|
||||
public static final int poisondam = NONE - 11;
|
||||
|
||||
static final int[] ENCODED_COUNT = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0
|
||||
@ -437,8 +448,35 @@ public class Stat {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public void add(Instance other) {
|
||||
// TODO
|
||||
/**
|
||||
* Encodings: param -- value (in bits)
|
||||
* 0 : 0 | X
|
||||
* 1 : Y | X
|
||||
* 2 : 6,10 | X
|
||||
* 3 : 6,10 | 8,8
|
||||
* 4 : 0 | 2,10,10
|
||||
*/
|
||||
public Instance add(Instance other) {
|
||||
int value1, value2, value3;
|
||||
switch (entry.Encode) {
|
||||
case 3:
|
||||
value1 = Math.min(value1() + other.value1(), (1 << 8) - 1);
|
||||
value2 = Math.min(value2() + other.value2(), (1 << 8) - 1);
|
||||
value = (value2 << 8) | value1;
|
||||
break;
|
||||
case 4:
|
||||
// TODO: see issue #24
|
||||
value2 = Math.min(value2() + other.value2(), (1 << 10) - 1);
|
||||
value3 = Math.min(value3() + other.value3(), (1 << 10) - 1);
|
||||
value = (value3 << 12) | (value2 << 2) | (value & 0x3);
|
||||
break;
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
default:
|
||||
value += other.value;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private static final StringBuilder builder = new StringBuilder(32);
|
||||
@ -729,4 +767,40 @@ public class Stat {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class Aggregate extends Instance {
|
||||
int encoding = 19;
|
||||
String str;
|
||||
String str2;
|
||||
Instance[] stats;
|
||||
Aggregate(int stat, String str, String str2, Instance... stats) {
|
||||
super(stat, 0);
|
||||
this.stats = stats;
|
||||
this.str = str;
|
||||
this.str2 = str2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(int unused1, int unused2, String unused3, String unused4, String unused5) {
|
||||
if (stats.length == 2) {
|
||||
if (stats[0].value == stats[1].value) {
|
||||
return Riiablo.string.format(str, stats[1].value);
|
||||
} else {
|
||||
return Riiablo.string.format(str2, stats[0].value, stats[1].value);
|
||||
}
|
||||
} else {
|
||||
assert stats.length == 3;
|
||||
if (stats[0].value == stats[1].value) {
|
||||
return Riiablo.string.format(str, stats[1].value, stats[2].value);
|
||||
} else {
|
||||
return Riiablo.string.format(str2, stats[0].value, stats[1].value, stats[2].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return stat + "(" + entry + ")" + "=" + Arrays.toString(stats) + " : " + format(0, 0, null, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user