v2.4.0: added initial logic for finding/creating trinkets

This commit is contained in:
Evan Debenham
2024-03-10 14:26:52 -04:00
parent 4bb583b24f
commit ae020c1f71
10 changed files with 322 additions and 45 deletions

View File

@@ -1290,6 +1290,14 @@ items.stones.stoneofintuition$wndguess.incorrect=Your guess was incorrect.
items.stones.stoneofshock.name=stone of shock items.stones.stoneofshock.name=stone of shock
items.stones.stoneofshock.desc=This runestone unleashes a blast of electrical energy which briefly stuns all nearby targets, and grants the thrower wand charge for each target hit. items.stones.stoneofshock.desc=This runestone unleashes a blast of electrical energy which briefly stuns all nearby targets, and grants the thrower wand charge for each target hit.
###trinkets
items.trinkets.trinketcatalyst.name=trinket catalyst
items.trinkets.trinketcatalyst.window_text=The water begins to glow as you use the catalyst. There are a few nearby items you could imbue with magical energy.
items.trinkets.trinketcatalyst.desc=TODO
items.trinkets.trinket$placeholder.name=trinket
###wands ###wands
items.wands.cursedwand.ondeath=You were killed by your own %s. items.wands.cursedwand.ondeath=You were killed by your own %s.
items.wands.cursedwand.nothing=Nothing happens. items.wands.cursedwand.nothing=Nothing happens.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -55,6 +55,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.spells.SummonElemental;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.TelekineticGrab; import com.shatteredpixel.shatteredpixeldungeon.items.spells.TelekineticGrab;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.UnstableSpell; import com.shatteredpixel.shatteredpixeldungeon.items.spells.UnstableSpell;
import com.shatteredpixel.shatteredpixeldungeon.items.spells.WildEnergy; import com.shatteredpixel.shatteredpixeldungeon.items.spells.WildEnergy;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.TrinketCatalyst;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand; import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWeapon;
import com.watabou.utils.Reflection; import com.watabou.utils.Reflection;
@@ -189,7 +190,8 @@ public abstract class Recipe {
new Recycle.Recipe(), new Recycle.Recipe(),
new TelekineticGrab.Recipe(), new TelekineticGrab.Recipe(),
new SummonElemental.Recipe(), new SummonElemental.Recipe(),
new StewedMeat.oneMeat() new StewedMeat.oneMeat(),
new TrinketCatalyst.Recipe()
}; };
private static Recipe[] twoIngredientRecipes = new Recipe[]{ private static Recipe[] twoIngredientRecipes = new Recipe[]{

View File

@@ -0,0 +1,56 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2024 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.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
public abstract class Trinket extends Item {
@Override
public boolean isIdentified() {
return true;
}
@Override
public boolean isUpgradable() {
return false;
}
public static class PlaceHolder extends Trinket {
{
image = ItemSpriteSheet.TRINKET_HOLDER;
}
@Override
public boolean isSimilar(Item item) {
return item instanceof Trinket;
}
@Override
public String info() {
return "";
}
}
}

View File

@@ -0,0 +1,171 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2024 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.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.items.Generator;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.AlchemyScene;
import com.shatteredpixel.shatteredpixeldungeon.scenes.PixelScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.ui.ItemButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.RedButton;
import com.shatteredpixel.shatteredpixeldungeon.ui.RenderedTextBlock;
import com.shatteredpixel.shatteredpixeldungeon.ui.Window;
import com.shatteredpixel.shatteredpixeldungeon.windows.IconTitle;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndInfoItem;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndSadGhost;
import java.util.ArrayList;
public class TrinketCatalyst extends Item {
{
image = ItemSpriteSheet.ARCANE_RESIN;
}
@Override
public boolean isIdentified() {
return true;
}
@Override
public boolean isUpgradable() {
return false;
}
public static class Recipe extends com.shatteredpixel.shatteredpixeldungeon.items.Recipe {
@Override
public boolean testIngredients(ArrayList<Item> ingredients) {
return ingredients.size() == 1 && ingredients.get(0) instanceof TrinketCatalyst;
}
@Override
public int cost(ArrayList<Item> ingredients) {
return 6;
}
@Override
public Item brew(ArrayList<Item> ingredients) {
ingredients.get(0).quantity(0);
//we silently re-add the catalyst so that we can clear it when a trinket is selected
//this way player isn't totally screwed if they quit the game while selecting
new TrinketCatalyst().collect();
ShatteredPixelDungeon.scene().addToFront(new WndTrinket());
return null;
}
@Override
public Item sampleOutput(ArrayList<Item> ingredients) {
return new Trinket.PlaceHolder();
}
}
public static class WndTrinket extends Window {
private static final int WIDTH = 120;
private static final int BTN_SIZE = 32;
private static final int BTN_GAP = 5;
private static final int GAP = 2;
private static final int NUM_TRINKETS = 3;
public WndTrinket(){
TrinketCatalyst cata = new TrinketCatalyst();
IconTitle titlebar = new IconTitle();
titlebar.icon(new ItemSprite(cata));
titlebar.label(Messages.titleCase(cata.name()));
titlebar.setRect(0, 0, WIDTH, 0);
add( titlebar );
RenderedTextBlock message = PixelScene.renderTextBlock( Messages.get(TrinketCatalyst.class, "window_text"), 6 );
message.maxWidth(WIDTH);
message.setPos(0, titlebar.bottom() + GAP);
add( message );
for (int i = 1; i <= NUM_TRINKETS; i++){
ItemButton btnReward = new ItemButton(){
@Override
protected void onClick() {
ShatteredPixelDungeon.scene().addToFront(new RewardWindow(item()));
}
};
btnReward.item(Generator.randomUsingDefaults());
btnReward.setRect( i*(WIDTH - BTN_GAP) / NUM_TRINKETS - BTN_SIZE, message.top() + message.height() + BTN_GAP, BTN_SIZE, BTN_SIZE );
add( btnReward );
}
resize(WIDTH, (int)(message.top() + message.height() + 2*BTN_GAP + BTN_SIZE));
}
@Override
public void onBackPressed() {
//do nothing
}
private class RewardWindow extends WndInfoItem {
public RewardWindow( Item item ) {
super(item);
RedButton btnConfirm = new RedButton(Messages.get(WndSadGhost.class, "confirm")){
@Override
protected void onClick() {
RewardWindow.this.hide();
WndTrinket.this.hide();
TrinketCatalyst cata = Dungeon.hero.belongings.getItem(TrinketCatalyst.class);
if (cata != null) {
cata.detach(Dungeon.hero.belongings.backpack);
((AlchemyScene)ShatteredPixelDungeon.scene()).craftItem(null, item);
}
}
};
btnConfirm.setRect(0, height+2, width/2-1, 16);
add(btnConfirm);
RedButton btnCancel = new RedButton(Messages.get(WndSadGhost.class, "cancel")){
@Override
protected void onClick() {
hide();
}
};
btnCancel.setRect(btnConfirm.right()+2, height+2, btnConfirm.width(), 16);
add(btnCancel);
resize(width, (int)btnCancel.bottom());
}
}
}
}

View File

@@ -67,6 +67,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfStrength;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade;
import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfEnchantment; import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfEnchantment;
import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfIntuition; import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfIntuition;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.TrinketCatalyst;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfRegrowth; import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfRegrowth;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfWarding; import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfWarding;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.HeavyBoomerang; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.HeavyBoomerang;
@@ -225,6 +226,10 @@ public abstract class Level implements Bundlable {
if ( Dungeon.depth == ((Dungeon.seed % 3) + 1)){ if ( Dungeon.depth == ((Dungeon.seed % 3) + 1)){
addItemToSpawn( new StoneOfIntuition() ); addItemToSpawn( new StoneOfIntuition() );
} }
if ( Dungeon.depth == (((Dungeon.seed+1) % 3) + 1)){
addItemToSpawn( new TrinketCatalyst());
}
if (Dungeon.depth > 1) { if (Dungeon.depth > 1) {
//50% chance of getting a level feeling //50% chance of getting a level feeling
@@ -774,6 +779,14 @@ public abstract class Level implements Bundlable {
return null; return null;
if (match == null){ if (match == null){
//if we have a trinket catalyst, always return that
for (Item i : itemsToSpawn){
if (i instanceof TrinketCatalyst){
itemsToSpawn.remove(i);
return i;
}
}
Item item = Random.element(itemsToSpawn); Item item = Random.element(itemsToSpawn);
itemsToSpawn.remove(item); itemsToSpawn.remove(item);
return item; return item;

View File

@@ -48,6 +48,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.journal.GuidePage;
import com.shatteredpixel.shatteredpixeldungeon.items.journal.RegionLorePage; import com.shatteredpixel.shatteredpixeldungeon.items.journal.RegionLorePage;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.GoldenKey; import com.shatteredpixel.shatteredpixeldungeon.items.keys.GoldenKey;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.Key; import com.shatteredpixel.shatteredpixeldungeon.items.keys.Key;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.TrinketCatalyst;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document; import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
import com.shatteredpixel.shatteredpixeldungeon.journal.Notes; import com.shatteredpixel.shatteredpixeldungeon.journal.Notes;
import com.shatteredpixel.shatteredpixeldungeon.levels.builders.Builder; import com.shatteredpixel.shatteredpixeldungeon.levels.builders.Builder;
@@ -406,7 +407,17 @@ public abstract class RegularLevel extends Level {
for (Item item : itemsToSpawn) { for (Item item : itemsToSpawn) {
int cell = randomDropCell(); int cell = randomDropCell();
drop( item, cell ).type = Heap.Type.HEAP; if (item instanceof TrinketCatalyst){
drop( item, cell ).type = Heap.Type.LOCKED_CHEST;
int keyCell = randomDropCell();
drop( new GoldenKey(Dungeon.depth), keyCell ).type = Heap.Type.HEAP;
if (map[keyCell] == Terrain.HIGH_GRASS || map[keyCell] == Terrain.FURROWED_GRASS) {
map[keyCell] = Terrain.GRASS;
losBlocking[keyCell] = false;
}
} else {
drop( item, cell ).type = Heap.Type.HEAP;
}
if (map[cell] == Terrain.HIGH_GRASS || map[cell] == Terrain.FURROWED_GRASS) { if (map[cell] == Terrain.HIGH_GRASS || map[cell] == Terrain.FURROWED_GRASS) {
map[cell] = Terrain.GRASS; map[cell] = Terrain.GRASS;
losBlocking[cell] = false; losBlocking[cell] = false;

View File

@@ -607,41 +607,9 @@ public class AlchemyScene extends PixelScene {
} }
if (result != null){ if (result != null){
bubbleEmitter.start(Speck.factory( Speck.BUBBLE ), 0.01f, 100 );
smokeEmitter.burst(Speck.factory( Speck.WOOL ), 10 );
Sample.INSTANCE.play( Assets.Sounds.PUFF );
int resultQuantity = result.quantity(); craftItem(ingredients, result);
if (!result.collect()){
Dungeon.level.drop(result, Dungeon.hero.pos);
}
Statistics.itemsCrafted++;
Badges.validateItemsCrafted();
try {
Dungeon.saveAll();
} catch (IOException e) {
ShatteredPixelDungeon.reportException(e);
}
synchronized (inputs) {
for (int i = 0; i < inputs.length; i++) {
if (inputs[i] != null && inputs[i].item() != null) {
Item item = inputs[i].item();
if (item.quantity() <= 0) {
inputs[i].item(null);
} else {
inputs[i].slot.updateText();
}
}
}
}
updateState();
//we reset the quantity in case the result was merged into another stack in the backpack
result.quantity(resultQuantity);
outputs[0].item(result);
} }
boolean foundItems = true; boolean foundItems = true;
@@ -657,6 +625,44 @@ public class AlchemyScene extends PixelScene {
repeat.enable(foundItems); repeat.enable(foundItems);
cancel.enable(false); cancel.enable(false);
} }
public void craftItem( ArrayList<Item> ingredients, Item result ){
bubbleEmitter.start(Speck.factory( Speck.BUBBLE ), 0.01f, 100 );
smokeEmitter.burst(Speck.factory( Speck.WOOL ), 10 );
Sample.INSTANCE.play( Assets.Sounds.PUFF );
int resultQuantity = result.quantity();
if (!result.collect()){
Dungeon.level.drop(result, Dungeon.hero.pos);
}
Statistics.itemsCrafted++;
Badges.validateItemsCrafted();
try {
Dungeon.saveAll();
} catch (IOException e) {
ShatteredPixelDungeon.reportException(e);
}
synchronized (inputs) {
for (int i = 0; i < inputs.length; i++) {
if (inputs[i] != null && inputs[i].item() != null) {
Item item = inputs[i].item();
if (item.quantity() <= 0) {
inputs[i].item(null);
} else {
inputs[i].slot.updateText();
}
}
}
}
updateState();
//we reset the quantity in case the result was merged into another stack in the backpack
result.quantity(resultQuantity);
outputs[0].item(result);
}
public void populate(ArrayList<Item> toFind, Belongings inventory){ public void populate(ArrayList<Item> toFind, Belongings inventory){
clearSlots(); clearSlots();

View File

@@ -51,13 +51,13 @@ public class ItemSpriteSheet {
public static final int WAND_HOLDER = PLACEHOLDERS+4; public static final int WAND_HOLDER = PLACEHOLDERS+4;
public static final int RING_HOLDER = PLACEHOLDERS+5; public static final int RING_HOLDER = PLACEHOLDERS+5;
public static final int ARTIFACT_HOLDER = PLACEHOLDERS+6; public static final int ARTIFACT_HOLDER = PLACEHOLDERS+6;
public static final int FOOD_HOLDER = PLACEHOLDERS+7; public static final int TRINKET_HOLDER = PLACEHOLDERS+7;
public static final int BOMB_HOLDER = PLACEHOLDERS+8; public static final int FOOD_HOLDER = PLACEHOLDERS+8;
public static final int POTION_HOLDER = PLACEHOLDERS+9; public static final int BOMB_HOLDER = PLACEHOLDERS+9;
public static final int SCROLL_HOLDER = PLACEHOLDERS+11; public static final int POTION_HOLDER = PLACEHOLDERS+10;
public static final int SEED_HOLDER = PLACEHOLDERS+10; public static final int SEED_HOLDER = PLACEHOLDERS+11;
public static final int STONE_HOLDER = PLACEHOLDERS+12; public static final int SCROLL_HOLDER = PLACEHOLDERS+12;
public static final int CATA_HOLDER = PLACEHOLDERS+13; public static final int STONE_HOLDER = PLACEHOLDERS+13;
public static final int ELIXIR_HOLDER = PLACEHOLDERS+14; public static final int ELIXIR_HOLDER = PLACEHOLDERS+14;
public static final int SPELL_HOLDER = PLACEHOLDERS+15; public static final int SPELL_HOLDER = PLACEHOLDERS+15;
static{ static{
@@ -68,13 +68,13 @@ public class ItemSpriteSheet {
assignItemRect(WAND_HOLDER, 14, 14); assignItemRect(WAND_HOLDER, 14, 14);
assignItemRect(RING_HOLDER, 8, 10); assignItemRect(RING_HOLDER, 8, 10);
assignItemRect(ARTIFACT_HOLDER, 15, 15); assignItemRect(ARTIFACT_HOLDER, 15, 15);
assignItemRect(TRINKET_HOLDER, 16, 11);
assignItemRect(FOOD_HOLDER, 15, 11); assignItemRect(FOOD_HOLDER, 15, 11);
assignItemRect(BOMB_HOLDER, 10, 13); assignItemRect(BOMB_HOLDER, 10, 13);
assignItemRect(POTION_HOLDER, 12, 14); assignItemRect(POTION_HOLDER, 12, 14);
assignItemRect(SEED_HOLDER, 10, 10); assignItemRect(SEED_HOLDER, 10, 10);
assignItemRect(SCROLL_HOLDER, 15, 14); assignItemRect(SCROLL_HOLDER, 15, 14);
assignItemRect(STONE_HOLDER, 14, 12); assignItemRect(STONE_HOLDER, 14, 12);
assignItemRect(CATA_HOLDER, 6, 15);
assignItemRect(ELIXIR_HOLDER, 12, 14); assignItemRect(ELIXIR_HOLDER, 12, 14);
assignItemRect(SPELL_HOLDER, 8, 16); assignItemRect(SPELL_HOLDER, 8, 16);
} }

View File

@@ -214,7 +214,17 @@ public class WndJournal extends WndTabbed {
private RedButton[] pageButtons; private RedButton[] pageButtons;
private static final int NUM_BUTTONS = 9; private static final int NUM_BUTTONS = 9;
private static final int[] spriteIndexes = {10, 12, 7, 9, 11, 8, 3, 14, 15}; private static final int[] sprites = {
ItemSpriteSheet.SEED_HOLDER,
ItemSpriteSheet.STONE_HOLDER,
ItemSpriteSheet.FOOD_HOLDER,
ItemSpriteSheet.POTION_HOLDER,
ItemSpriteSheet.SCROLL_HOLDER,
ItemSpriteSheet.BOMB_HOLDER,
ItemSpriteSheet.MISSILE_HOLDER,
ItemSpriteSheet.ELIXIR_HOLDER,
ItemSpriteSheet.SPELL_HOLDER
};
public static int currentPageIdx = 0; public static int currentPageIdx = 0;
@@ -237,7 +247,7 @@ public class WndJournal extends WndTabbed {
} }
}; };
if (Document.ALCHEMY_GUIDE.isPageFound(i)) { if (Document.ALCHEMY_GUIDE.isPageFound(i)) {
pageButtons[i].icon(new ItemSprite(ItemSpriteSheet.SOMETHING + spriteIndexes[i], null)); pageButtons[i].icon(new ItemSprite(sprites[i], null));
} else { } else {
pageButtons[i].icon(new ItemSprite(ItemSpriteSheet.SOMETHING, null)); pageButtons[i].icon(new ItemSprite(ItemSpriteSheet.SOMETHING, null));
pageButtons[i].enable(false); pageButtons[i].enable(false);