v3.3.0: implemented the cracked spyglass trinket (with WIP art)

This also involved repositioning some item sprites, and changing trinket crafting to show the 4th option
This commit is contained in:
Evan Debenham
2025-11-12 14:09:26 -05:00
parent 73b7f8c8e9
commit 640fb1056a
9 changed files with 127 additions and 30 deletions

View File

@@ -1404,6 +1404,12 @@ items.trinkets.chaoticcenser.desc=After some time in the alchemy pot this incens
items.trinkets.chaoticcenser.typical_stats_desc=Typically this trinket will spawn a harmful gas near an enemy roughly every _%d_ turns. Gasses will only appear when enemies are present. At higher levels these gases are more likely to be exotic and powerful.
items.trinkets.chaoticcenser.stats_desc=At its current level, this trinket will spawn a harmful gas near an enemy roughly every _%d_ turns. Gasses will only appear when enemies are present. At higher levels these gases are more likely to be exotic and powerful.
items.trinkets.crackedspyglass.name=cracked spyglass
items.trinkets.crackedspyglass.desc=This handheld telescope would be a demonstration of great and precise workmanship, if it weren't for the crack along its front lens. The spyglass seems to be helping you by revealing new items in the dungeon, but due to its flaw the revealed items are quite hard to see!
items.trinkets.crackedspyglass.typical_stats_desc=Typically this trinket has a _%1$s%% chance_ to create an extra hidden item on each non-boss floor.
items.trinkets.crackedspyglass.stats_desc=At its current level, this trinket has a _%1$s%% chance_ to create an extra hidden item on each non-boss floor.
items.trinkets.crackedspyglass.stats_desc_upgraded=At its current level, this trinket has a _100%% chance_ to create an extra hidden item on each non-boss floor, and a _%1$s%% chance_ to create a second extra hidden item.
items.trinkets.dimensionalsundial.name=dimensional sundial
items.trinkets.dimensionalsundial.warning=Your sundial isn't casting a shadow, you feel uneasy.
items.trinkets.dimensionalsundial.desc=This small handheld sundial is somehow able to cast a shadow in the depths of the dungeon, even if you aren't holding it upright. Even more strangely, the shadow's position seems to have no relation to the sun in this world. When no shadow is cast, the sundial seems to attract danger.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -110,6 +110,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfFlock;
import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfIntuition;
import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfShock;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.ChaoticCenser;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.CrackedSpyglass;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.DimensionalSundial;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.ExoticCrystals;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.EyeOfNewt;
@@ -590,9 +591,10 @@ public class Generator {
VialOfBlood.class,
ShardOfOblivion.class,
ChaoticCenser.class,
FerretTuft.class
FerretTuft.class,
CrackedSpyglass.class
};
TRINKET.defaultProbs = new float[]{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
TRINKET.defaultProbs = new float[]{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
TRINKET.probs = TRINKET.defaultProbs.clone();
for (Category cat : Category.values()){

View File

@@ -77,6 +77,7 @@ public class Heap implements Bundlable {
public boolean seen = false;
public boolean haunted = false;
public boolean autoExplored = false; //used to determine if this heap should count for exploration bonus
public boolean hidden = false; //sets alpha to 15%
public LinkedList<Item> items = new LinkedList<>();
@@ -150,6 +151,7 @@ public class Heap implements Bundlable {
}
public void drop( Item item ) {
hidden = false;
if (item.stackable && type != Type.FOR_SALE) {
@@ -183,6 +185,7 @@ public class Heap implements Bundlable {
}
public void replace( Item a, Item b ) {
hidden = false;
int index = items.indexOf( a );
if (index != -1) {
items.remove( index );
@@ -197,6 +200,7 @@ public class Heap implements Bundlable {
}
public void remove( Item a ){
hidden = false;
items.remove(a);
if (items.isEmpty()){
destroy();
@@ -206,6 +210,7 @@ public class Heap implements Bundlable {
}
public void burn() {
hidden = false;
if (type != Type.HEAP) {
return;
@@ -257,6 +262,7 @@ public class Heap implements Bundlable {
//Note: should not be called to initiate an explosion, but rather by an explosion that is happening.
public void explode() {
hidden = false;
//breaks open most standard containers, mimics die.
if (type == Type.CHEST || type == Type.SKELETON) {
@@ -415,6 +421,7 @@ public class Heap implements Bundlable {
private static final String ITEMS = "items";
private static final String HAUNTED = "haunted";
private static final String AUTO_EXPLORED = "auto_explored";
private static final String HIDDEN = "hidden";
@SuppressWarnings("unchecked")
@Override
@@ -440,6 +447,7 @@ public class Heap implements Bundlable {
haunted = bundle.getBoolean( HAUNTED );
autoExplored = bundle.getBoolean( AUTO_EXPLORED );
hidden = bundle.getBoolean( HIDDEN );
}
@Override
@@ -450,6 +458,7 @@ public class Heap implements Bundlable {
bundle.put( ITEMS, items );
bundle.put( HAUNTED, haunted );
bundle.put( AUTO_EXPLORED, autoExplored );
bundle.put( HIDDEN, hidden );
}
}

View File

@@ -0,0 +1,64 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2025 Evan Debenham
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.items.trinkets;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
public class CrackedSpyglass extends Trinket{
{
image = ItemSpriteSheet.SPYGLASS;
}
@Override
protected int upgradeEnergyCost() {
//6 -> 8(14) -> 10(24) -> 12(36)
return 6+2*level();
}
@Override
public String statsDesc() {
if (isIdentified()){
if (buffedLvl() >= 2){
return Messages.get(this, "stats_desc_upgraded", Messages.decimalFormat("#.##", 100 * (extraLootChance(buffedLvl())-1f)));
} else {
return Messages.get(this, "stats_desc", Messages.decimalFormat("#.##", 100 * extraLootChance(buffedLvl())));
}
} else {
return Messages.get(this, "typical_stats_desc", Messages.decimalFormat("#.##", 100 * extraLootChance(0)));
}
}
public static float extraLootChance(){
return extraLootChance(trinketLevel(CrackedSpyglass.class));
}
public static float extraLootChance(int level ){
if (level <= -1){
return 0;
} else {
return 0.4f*(level+1);
}
}
}

View File

@@ -158,7 +158,7 @@ public class TrinketCatalyst extends Item {
private static final int BTN_GAP = 4;
private static final int GAP = 2;
private static final int NUM_TRINKETS = 4; //last one is a random choice
private static final int NUM_TRINKETS = 4;
public WndTrinket( TrinketCatalyst cata ){
@@ -174,7 +174,7 @@ public class TrinketCatalyst extends Item {
add( message );
//roll new trinkets if trinkets were not already rolled
while (cata.rolledTrinkets.size() < NUM_TRINKETS-1){
while (cata.rolledTrinkets.size() < NUM_TRINKETS){
cata.rolledTrinkets.add((Trinket) Generator.random(Generator.Category.TRINKET));
}
@@ -185,11 +185,7 @@ public class TrinketCatalyst extends Item {
ShatteredPixelDungeon.scene().addToFront(new RewardWindow(item()));
}
};
if (i == NUM_TRINKETS-1){
btnReward.item(new RandomTrinket());
} else {
btnReward.item(cata.rolledTrinkets.get(i));
}
btnReward.item(cata.rolledTrinkets.get(i));
btnReward.setRect( (i+1)*(WIDTH - BTN_GAP) / NUM_TRINKETS - BTN_SIZE, message.top() + message.height() + BTN_GAP, BTN_SIZE, BTN_SIZE );
add( btnReward );

View File

@@ -50,6 +50,7 @@ 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.CrackedSpyglass;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.MimicTooth;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.TrinketCatalyst;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
@@ -675,6 +676,19 @@ public abstract class RegularLevel extends Level {
}
Random.popGenerator();
//extra spyglass loot
Random.pushGenerator(Random.Long());
int items = (int)(Random.Float() + CrackedSpyglass.extraLootChance());
for (int i = 0; i < items; i++){
int cell = randomDropCell();
if (map[cell] == Terrain.HIGH_GRASS || map[cell] == Terrain.FURROWED_GRASS) {
map[cell] = Terrain.GRASS;
losBlocking[cell] = false;
}
drop( Generator.randomUsingDefaults(), cell).hidden = true;
}
Random.popGenerator();
}
private static HashMap<Document, Dungeon.LimitedDrops> limitedDocs = new HashMap<>();

View File

@@ -213,25 +213,31 @@ public class ItemSprite extends MovieClip {
if (heap.size() <= 0 || heap.items == null){
return view( 0, null );
}
switch (heap.type) {
case HEAP: case FOR_SALE:
return view( heap.peek() );
view( heap.peek() ); break;
case CHEST:
return view( ItemSpriteSheet.CHEST, null );
view( ItemSpriteSheet.CHEST, null ); break;
case LOCKED_CHEST:
return view( ItemSpriteSheet.LOCKED_CHEST, null );
view( ItemSpriteSheet.LOCKED_CHEST, null ); break;
case CRYSTAL_CHEST:
return view( ItemSpriteSheet.CRYSTAL_CHEST, null );
view( ItemSpriteSheet.CRYSTAL_CHEST, null ); break;
case TOMB:
return view( ItemSpriteSheet.TOMB, null );
view( ItemSpriteSheet.TOMB, null ); break;
case SKELETON:
return view( ItemSpriteSheet.BONES, null );
view( ItemSpriteSheet.BONES, null ); break;
case REMAINS:
return view( ItemSpriteSheet.REMAINS, null );
view( ItemSpriteSheet.REMAINS, null ); break;
default:
return view( 0, null );
view( 0, null );
}
if (heap.hidden){
alpha(0.15f);
}
return this;
}
public ItemSprite view( int image, Glowing glowing ) {

View File

@@ -434,7 +434,7 @@ public class ItemSpriteSheet {
assignItemRect(i, 8, 10);
}
private static final int ARTIFACTS = xy(1, 16); //24 slots
private static final int ARTIFACTS = xy(1, 16); //32 slots
public static final int ARTIFACT_CLOAK = ARTIFACTS+0;
public static final int ARTIFACT_ARMBAND = ARTIFACTS+1;
public static final int ARTIFACT_CAPE = ARTIFACTS+2;
@@ -486,7 +486,7 @@ public class ItemSpriteSheet {
assignItemRect(ARTIFACT_TOME, 14, 16);
}
private static final int TRINKETS = xy(9, 17); //24 slots
private static final int TRINKETS = xy(1, 18); //32 slots
public static final int RAT_SKULL = TRINKETS+0;
public static final int PARCHMENT_SCRAP = TRINKETS+1;
public static final int PETRIFIED_SEED = TRINKETS+2;
@@ -503,6 +503,7 @@ public class ItemSpriteSheet {
public static final int OBLIVION_SHARD = TRINKETS+13;
public static final int CHAOTIC_CENSER = TRINKETS+14;
public static final int FERRET_TUFT = TRINKETS+15;
public static final int SPYGLASS = TRINKETS+16;
static{
assignItemRect(RAT_SKULL, 16, 11);
assignItemRect(PARCHMENT_SCRAP, 10, 14);
@@ -520,9 +521,10 @@ public class ItemSpriteSheet {
assignItemRect(OBLIVION_SHARD, 7, 14);
assignItemRect(CHAOTIC_CENSER, 13, 15);
assignItemRect(FERRET_TUFT, 16, 15);
assignItemRect(SPYGLASS, 15, 15);
}
private static final int SCROLLS = xy(1, 19); //16 slots
private static final int SCROLLS = xy(1, 20); //16 slots
public static final int SCROLL_KAUNAN = SCROLLS+0;
public static final int SCROLL_SOWILO = SCROLLS+1;
public static final int SCROLL_LAGUZ = SCROLLS+2;
@@ -543,7 +545,7 @@ public class ItemSpriteSheet {
assignItemRect(ARCANE_RESIN , 12, 11);
}
private static final int EXOTIC_SCROLLS = xy(1, 20); //16 slots
private static final int EXOTIC_SCROLLS = xy(1, 21); //16 slots
public static final int EXOTIC_KAUNAN = EXOTIC_SCROLLS+0;
public static final int EXOTIC_SOWILO = EXOTIC_SCROLLS+1;
public static final int EXOTIC_LAGUZ = EXOTIC_SCROLLS+2;
@@ -561,7 +563,7 @@ public class ItemSpriteSheet {
assignItemRect(i, 15, 14);
}
private static final int STONES = xy(1, 21); //16 slots
private static final int STONES = xy(1, 22); //16 slots
public static final int STONE_AGGRESSION = STONES+0;
public static final int STONE_AUGMENTATION = STONES+1;
public static final int STONE_FEAR = STONES+2;
@@ -579,7 +581,7 @@ public class ItemSpriteSheet {
assignItemRect(i, 14, 12);
}
private static final int POTIONS = xy(1, 22); //16 slots
private static final int POTIONS = xy(1, 23); //16 slots
public static final int POTION_CRIMSON = POTIONS+0;
public static final int POTION_AMBER = POTIONS+1;
public static final int POTION_GOLDEN = POTIONS+2;
@@ -600,7 +602,7 @@ public class ItemSpriteSheet {
assignItemRect(LIQUID_METAL, 8, 15);
}
private static final int EXOTIC_POTIONS = xy(1, 23); //16 slots
private static final int EXOTIC_POTIONS = xy(1, 24); //16 slots
public static final int EXOTIC_CRIMSON = EXOTIC_POTIONS+0;
public static final int EXOTIC_AMBER = EXOTIC_POTIONS+1;
public static final int EXOTIC_GOLDEN = EXOTIC_POTIONS+2;
@@ -618,7 +620,7 @@ public class ItemSpriteSheet {
assignItemRect(i, 12, 13);
}
private static final int SEEDS = xy(1, 24); //16 slots
private static final int SEEDS = xy(1, 25); //16 slots
public static final int SEED_ROTBERRY = SEEDS+0;
public static final int SEED_FIREBLOOM = SEEDS+1;
public static final int SEED_SWIFTTHISTLE = SEEDS+2;
@@ -636,7 +638,7 @@ public class ItemSpriteSheet {
assignItemRect(i, 10, 10);
}
private static final int BREWS = xy(1, 25); //8 slots
private static final int BREWS = xy(1, 26); //8 slots
public static final int BREW_INFERNAL = BREWS+0;
public static final int BREW_BLIZZARD = BREWS+1;
public static final int BREW_SHOCKING = BREWS+2;
@@ -644,7 +646,7 @@ public class ItemSpriteSheet {
public static final int BREW_AQUA = BREWS+4;
public static final int BREW_UNSTABLE = BREWS+5;
private static final int ELIXIRS = xy(9, 25); //8 slots
private static final int ELIXIRS = xy(9, 26); //8 slots
public static final int ELIXIR_HONEY = ELIXIRS+0;
public static final int ELIXIR_AQUA = ELIXIRS+1;
public static final int ELIXIR_MIGHT = ELIXIRS+2;
@@ -660,8 +662,6 @@ public class ItemSpriteSheet {
assignItemRect(BREW_AQUA, 9, 11);
}
//16 free slots
private static final int SPELLS = xy(1, 27); //16 slots
public static final int WILD_ENERGY = SPELLS+0;
public static final int PHASE_SHIFT = SPELLS+1;