v3.1.0: overhauled floor explore logic, now allows partial score
This commit is contained in:
@@ -273,11 +273,11 @@ windows.wndscorebreakdown.treasure_title=Treasure
|
||||
windows.wndscorebreakdown.treasure_desc=Based on gold collected and held item value.
|
||||
windows.wndscorebreakdown.treasure_desc_old=Based on gold collected.
|
||||
windows.wndscorebreakdown.explore_title=Exploration
|
||||
windows.wndscorebreakdown.explore_desc=Based on floors with all items found, all secrets found, and all puzzles solved.
|
||||
windows.wndscorebreakdown.explore_desc=Based on floors explored. Reduced by missing items or hidden doors, or unsolved puzzles.
|
||||
windows.wndscorebreakdown.bosses_title=Bosses
|
||||
windows.wndscorebreakdown.bosses_desc=Based on bosses defeated, reduced by standing in avoidable attacks during boss fights.
|
||||
windows.wndscorebreakdown.bosses_desc=Based on bosses defeated. Reduced by standing in avoidable attacks during boss fights.
|
||||
windows.wndscorebreakdown.quests_title=Quests
|
||||
windows.wndscorebreakdown.quests_desc=Based on quests completed.
|
||||
windows.wndscorebreakdown.quests_desc=Based on quests completed. Reduced by taking avoidable attacks or missing quest score.
|
||||
windows.wndscorebreakdown.win_multiplier=Win Multiplier
|
||||
windows.wndscorebreakdown.challenge_multiplier=Challenge Multiplier
|
||||
windows.wndscorebreakdown.total=Total Score
|
||||
|
||||
@@ -882,7 +882,7 @@ public class Dungeon {
|
||||
|
||||
public static void updateLevelExplored(){
|
||||
if (branch == 0 && level instanceof RegularLevel && !Dungeon.bossLevel()){
|
||||
Statistics.floorsExplored.put( depth, level.isLevelExplored(depth));
|
||||
Statistics.floorsExplored.put( depth, level.levelExplorePercent(depth));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,8 +189,8 @@ public enum Rankings {
|
||||
|
||||
Statistics.exploreScore = 0;
|
||||
int scorePerFloor = Statistics.floorsExplored.size * 50;
|
||||
for (Boolean b : Statistics.floorsExplored.valueList()){
|
||||
if (b) Statistics.exploreScore += scorePerFloor;
|
||||
for (float percentExplored : Statistics.floorsExplored.valueList()){
|
||||
Statistics.exploreScore += Math.round(percentExplored*scorePerFloor);
|
||||
}
|
||||
|
||||
Statistics.totalBossScore = 0;
|
||||
|
||||
@@ -45,7 +45,7 @@ public class Statistics {
|
||||
public static int progressScore;
|
||||
public static int heldItemValue;
|
||||
public static int treasureScore;
|
||||
public static SparseArray<Boolean> floorsExplored = new SparseArray<>();
|
||||
public static SparseArray<Float> floorsExplored = new SparseArray<>();
|
||||
public static int exploreScore;
|
||||
public static int[] bossScores = new int[5];
|
||||
public static int totalBossScore;
|
||||
@@ -128,7 +128,7 @@ public class Statistics {
|
||||
private static final String PROG_SCORE = "prog_score";
|
||||
private static final String ITEM_VAL = "item_val";
|
||||
private static final String TRES_SCORE = "tres_score";
|
||||
private static final String FLR_EXPL = "flr_expl";
|
||||
private static final String FLR_EXPL = "flr_expl_";
|
||||
private static final String EXPL_SCORE = "expl_score";
|
||||
private static final String BOSS_SCORES = "boss_scores";
|
||||
private static final String TOT_BOSS = "tot_boss";
|
||||
@@ -223,7 +223,10 @@ public class Statistics {
|
||||
floorsExplored.clear();
|
||||
for (int i = 1; i < 26; i++){
|
||||
if (bundle.contains( FLR_EXPL+i )){
|
||||
floorsExplored.put(i, bundle.getBoolean( FLR_EXPL+i ));
|
||||
floorsExplored.put(i, bundle.getFloat( FLR_EXPL+i ));
|
||||
//pre-3.1 saves. The bundle key does have an underscore and is a boolean
|
||||
} else if (bundle.contains( "flr_expl"+1 )){
|
||||
floorsExplored.put(i, bundle.getBoolean( "flr_expl"+i ) ? 1f : 0f);
|
||||
}
|
||||
}
|
||||
exploreScore = bundle.getInt( EXPL_SCORE );
|
||||
|
||||
@@ -1471,8 +1471,8 @@ public abstract class Level implements Bundlable {
|
||||
|
||||
}
|
||||
|
||||
public boolean isLevelExplored( int depth ){
|
||||
return false;
|
||||
public float levelExplorePercent( int depth ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int distance( int a, int b ) {
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.food.SupplyRation;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.journal.DocumentPage;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.journal.GuidePage;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.journal.RegionLorePage;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.keys.CrystalKey;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.keys.GoldenKey;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.keys.Key;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.MimicTooth;
|
||||
@@ -61,8 +62,10 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.secret.SecretRoom;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.MagicalFireRoom;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.PitRoom;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.SacrificeRoom;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.ShopRoom;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.SpecialRoom;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.StatueRoom;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.StandardRoom;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.entrance.EntranceRoom;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.exit.ExitRoom;
|
||||
@@ -85,6 +88,7 @@ import com.watabou.utils.Random;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
public abstract class RegularLevel extends Level {
|
||||
@@ -674,11 +678,16 @@ public abstract class RegularLevel extends Level {
|
||||
|
||||
protected Room randomRoom( Class<?extends Room> type ) {
|
||||
Random.shuffle( rooms );
|
||||
return room( type );
|
||||
}
|
||||
|
||||
public Room room (Class<?extends Room> type){
|
||||
for (Room r : rooms) {
|
||||
if (type.isInstance(r)) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -753,57 +762,98 @@ public abstract class RegularLevel extends Level {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLevelExplored( int depth ) {
|
||||
//A level is considered fully explored if:
|
||||
public float levelExplorePercent( int depth ) {
|
||||
//A room is considered not explored if:
|
||||
HashSet<Room> missedRooms = new HashSet<>();
|
||||
|
||||
//There are no levelgen heaps which are undiscovered, in an openable container, or which contain keys
|
||||
//There are levelgen heaps which are undiscovered, in an openable container, or which contain keys
|
||||
for (Heap h : heaps.valueList()){
|
||||
if (h.autoExplored) continue;
|
||||
|
||||
//we ignore crystal chests too as not all are openable
|
||||
if (!h.seen || (h.type != Heap.Type.HEAP && h.type != Heap.Type.FOR_SALE && h.type != Heap.Type.CRYSTAL_CHEST)){
|
||||
return false;
|
||||
}
|
||||
for (Item i : h.items){
|
||||
if (i instanceof Key){
|
||||
return false;
|
||||
missedRooms.add(room(h.pos));
|
||||
} else {
|
||||
for (Item i : h.items){
|
||||
if (i instanceof Key){
|
||||
missedRooms.add(room(h.pos));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//There is no magical fire or sacrificial fire
|
||||
//There is magical fire (blocks items) or sacrificial fire (contains items) in it
|
||||
for (Blob b : blobs.values()){
|
||||
if (b.volume > 0 && (b instanceof MagicalFireRoom.EternalFire || b instanceof SacrificialFire)){
|
||||
return false;
|
||||
if (b.volume > 0) {
|
||||
if (b instanceof MagicalFireRoom.EternalFire) {
|
||||
missedRooms.add(room(MagicalFireRoom.class));
|
||||
} else if (b instanceof SacrificialFire) {
|
||||
missedRooms.add(room(SacrificeRoom.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//There are no statues or mimics (unless they were made allies)
|
||||
//There are undefeated statues or mimics in it
|
||||
for (Mob m : mobs.toArray(new Mob[0])){
|
||||
if (m.alignment != Char.Alignment.ALLY){
|
||||
if (m instanceof Statue && ((Statue) m).levelGenStatue){
|
||||
return false;
|
||||
missedRooms.add(room(StatueRoom.class)); //use room the statue came from
|
||||
} else if (m instanceof Mimic){
|
||||
return false;
|
||||
missedRooms.add(room(m.pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//There are no barricades, locked doors, or hidden doors
|
||||
//it contains a barricade, locked door, or hidden door
|
||||
for (int i = 0; i < length; i++){
|
||||
if (map[i] == Terrain.BARRICADE || map[i] == Terrain.LOCKED_DOOR || map[i] == Terrain.SECRET_DOOR){
|
||||
return false;
|
||||
//we use adjacent cells to find the room this is connected to
|
||||
// we ignore connection rooms and prefer rooms already missed
|
||||
// note that if the tile borders two non-connection rooms, it only counts one
|
||||
Room candidate = null;
|
||||
for (int j : PathFinder.NEIGHBOURS4){
|
||||
if (room(i+j) != null){
|
||||
if (candidate == null || !missedRooms.contains(candidate)){
|
||||
candidate = room(i+j);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (candidate != null) {
|
||||
missedRooms.add(candidate);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//There are no unused keys for this depth in the journal
|
||||
//There are unused crystal keys for this room (only one crystal key room can be on each floor)
|
||||
// we ignore regular and golden keys as earlier checks would have already caught them
|
||||
for (Notes.KeyRecord rec : Notes.getRecords(Notes.KeyRecord.class)){
|
||||
if (rec.depth() == depth){
|
||||
return false;
|
||||
if (rec.depth() == depth && rec.type() == CrystalKey.class){
|
||||
for (Room r : rooms()){
|
||||
if (SpecialRoom.CRYSTAL_KEY_SPECIALS.contains(r.getClass())){
|
||||
missedRooms.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Note that it is NOT required for the player to see every tile or discover every trap.
|
||||
return true;
|
||||
|
||||
//score is reduced by 50%/30%/20% for each room missed
|
||||
// at 3 rooms missed this gives a score of 0 for the floor.
|
||||
//Yes this is a bit harsh, but it's to preserve balance from older versions
|
||||
// where a single missed room gave a score of 0.
|
||||
switch (missedRooms.size()){
|
||||
case 0:
|
||||
return 1f;
|
||||
case 1:
|
||||
return 0.5f;
|
||||
case 2:
|
||||
return 0.2f;
|
||||
default:
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -94,7 +94,7 @@ public abstract class SpecialRoom extends Room {
|
||||
) );
|
||||
|
||||
//only one special that uses crystal keys per floor
|
||||
private static final ArrayList<Class<? extends SpecialRoom>> CRYSTAL_KEY_SPECIALS = new ArrayList<>( Arrays.asList(
|
||||
public static final ArrayList<Class<? extends SpecialRoom>> CRYSTAL_KEY_SPECIALS = new ArrayList<>( Arrays.asList(
|
||||
PitRoom.class, CrystalVaultRoom.class, CrystalChoiceRoom.class, CrystalPathRoom.class
|
||||
) );
|
||||
|
||||
|
||||
Reference in New Issue
Block a user