v2.4.0: roughly implemented two more trinkets

This commit is contained in:
Evan Debenham
2024-03-15 14:55:26 -04:00
parent c80fa10980
commit 3fba4c5841
13 changed files with 199 additions and 11 deletions

View File

@@ -1291,6 +1291,15 @@ items.stones.stoneofshock.desc=This runestone unleashes a blast of electrical en
###trinkets
items.trinkets.exoticcrystal.name=exotic crystal
items.trinkets.exoticcrystal.desc=...\n\nAt its current level it will replace %d%% of potion or scroll drops with their exotic equivalents. This trinket does not affect potions of strength or scrolls of upgrade.
items.trinkets.parchmentscrap.name=parchment scrap
items.trinkets.parchmentscrap.desc=This little scrap of parchment looks like it came from a scroll. It has regained some of its magic, and it seems to be influencing weapons and armor found in the dungeon.\n\nAt its current level it will make enchantments/glyphs _%dx_ as common, and curses on weapons and armor _%sx_ as common. Curses on wands, rings, or artifacts are not affected.
items.trinkets.petrifiedseed.name=petrified seed
items.trinkets.petrifiedseed.desc=This seed has been fossilised, either by slow geological processes or by magic. The seed seems to be magically influencing the flora of the dungeon, occasionally replacing plant seeds with runestones.\n\nAt its current level it will cause trampled grass to drop runestones instead of seeds _%1$d%%_ of the time, and will also cause high grass to drop items _%2$d%%_ more often.
items.trinkets.ratskull.name=rat skull
items.trinkets.ratskull.desc=This macabre trinket isn't much larger than the skull of a normal rat, which is somehow a rarity down in this dungeon. The skull's magical influence seems to attract the more rare denizens of the dungeon, making them appear far more often.\n\nAt its current level it will make rare exotic enemies _%dx_ as likely to appear. The skull is only half as effective at attracting crystal mimics and armored statues, however.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -31,6 +31,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor;
import com.shatteredpixel.shatteredpixeldungeon.items.quest.DarkGold;
import com.shatteredpixel.shatteredpixeldungeon.items.quest.Pickaxe;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.ParchmentScrap;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
import com.shatteredpixel.shatteredpixeldungeon.journal.Notes;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room;
@@ -466,7 +467,7 @@ public class Blacksmith extends NPC {
// 30% base chance to be enchanted, stored separately so status isn't revealed early
float enchantRoll = Random.Float();
if (enchantRoll <= 0.3f){
if (enchantRoll <= 0.3f * ParchmentScrap.enchantChanceMultiplier()){
smithEnchant = Weapon.Enchantment.random();
smithGlyph = Armor.Glyph.random();
}

View File

@@ -37,6 +37,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.armor.LeatherArmor;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.MailArmor;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.PlateArmor;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.ScaleArmor;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.ParchmentScrap;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.Weapon;
import com.shatteredpixel.shatteredpixeldungeon.journal.Notes;
import com.shatteredpixel.shatteredpixeldungeon.levels.SewerLevel;
@@ -349,7 +350,7 @@ public class Ghost extends NPC {
// 20% base chance to be enchanted, stored separately so status isn't revealed early
float enchantRoll = Random.Float();
if (enchantRoll < 0.20f){
if (enchantRoll < 0.2f * ParchmentScrap.enchantChanceMultiplier()){
enchant = Weapon.Enchantment.random();
glyph = Armor.Glyph.random();
}

View File

@@ -100,6 +100,10 @@ import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfFear;
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.ParchmentScrap;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.PetrifiedSeed;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.RatSkull;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.Trinket;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfBlastWave;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfCorrosion;
@@ -186,6 +190,8 @@ import java.util.LinkedHashMap;
public class Generator {
public enum Category {
TRINKET ( 0, 0, Trinket.class),
WEAPON ( 2, 2, MeleeWeapon.class),
WEP_T1 ( 0, 0, MeleeWeapon.class),
WEP_T2 ( 0, 0, MeleeWeapon.class),
@@ -509,6 +515,16 @@ public class Generator {
ARTIFACT.defaultProbs = new float[]{ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 };
ARTIFACT.probs = ARTIFACT.defaultProbs.clone();
//Trinkets are unique like artifacts, but unlike them you can only have one at once
//So we don't need the same enforcement of uniqueness
TRINKET.classes = new Class<?>[]{
RatSkull.class,
ParchmentScrap.class,
PetrifiedSeed.class
};
TRINKET.defaultProbs = new float[]{ 1, 1, 1 };
TRINKET.probs = TRINKET.defaultProbs.clone();
for (Category cat : Category.values()){
if (cat.defaultProbs2 != null){
cat.defaultProbsTotal = new float[cat.defaultProbs.length];

View File

@@ -58,6 +58,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.armor.glyphs.Swiftness;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.glyphs.Thorns;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.glyphs.Viscosity;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfArcana;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.ParchmentScrap;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.HeroSprite;
@@ -545,10 +546,10 @@ public class Armor extends EquipableItem {
//30% chance to be cursed
//15% chance to be inscribed
float effectRoll = Random.Float();
if (effectRoll < 0.3f) {
if (effectRoll < 0.3f * ParchmentScrap.curseChanceMultiplier()) {
inscribe(Glyph.randomCurse());
cursed = true;
} else if (effectRoll >= 0.85f){
} else if (effectRoll >= 1f - (0.15f * ParchmentScrap.enchantChanceMultiplier())){
inscribe();
}

View File

@@ -0,0 +1,80 @@
/*
* 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.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
public class ParchmentScrap extends Trinket {
{
image = ItemSpriteSheet.PARCHMENT_SCRAP;
}
@Override
protected int upgradeEnergyCost() {
return 1 + level(); //TODO
}
@Override
public String desc() {
return Messages.get(this, "desc", (int)enchantChanceMultiplier(buffedLvl()), Messages.decimalFormat("#.##", curseChanceMultiplier(buffedLvl())));
}
public static float enchantChanceMultiplier(){
return enchantChanceMultiplier(trinketLevel(ParchmentScrap.class));
}
public static float enchantChanceMultiplier( int level ){
switch (level){
default:
return 1;
case 0:
return 2;
case 1:
return 4;
case 2:
return 7;
case 3:
return 10;
}
}
public static float curseChanceMultiplier(){
return curseChanceMultiplier(trinketLevel(ParchmentScrap.class));
}
public static float curseChanceMultiplier( int level ){
switch (level){
default:
return 1;
case 0:
return 1.5f;
case 1:
return 2f;
case 2:
return 1f;
case 3:
return 0f;
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
public class PetrifiedSeed extends Trinket {
{
image = ItemSpriteSheet.PETRIFIED_SEED;
}
@Override
protected int upgradeEnergyCost() {
return 1 + level(); //TODO
}
@Override
public String desc() {
return Messages.get(this, "desc", (int)(100*stoneInsteadOfSeedChance(buffedLvl())), (int)Math.round(100*(grassLootMultiplier(buffedLvl())-1f)));
}
public static float grassLootMultiplier(){
return grassLootMultiplier(trinketLevel(PetrifiedSeed.class));
}
public static float grassLootMultiplier( int level ){
return 1f + .4f*level/3f;
}
public static float stoneInsteadOfSeedChance(){
return stoneInsteadOfSeedChance(trinketLevel(PetrifiedSeed.class));
}
public static float stoneInsteadOfSeedChance( int level ){
if (level == -1){
return 0f;
} else {
return 0.35f + .05f*level;
}
}
}

View File

@@ -32,7 +32,7 @@ public class RatSkull extends Trinket {
@Override
protected int upgradeEnergyCost() {
return 5 + 10*level();
return 1 + level(); //TODO
}
@Override

View File

@@ -119,7 +119,9 @@ public class TrinketCatalyst extends Item {
ShatteredPixelDungeon.scene().addToFront(new RewardWindow(item()));
}
};
btnReward.item(Generator.randomUsingDefaults());
//TODO we need to persist these through save/load in case someone quits when the window is up
//alternatively we could just 'peek' at the items and then actually remove them when one is awarded.
btnReward.item(Generator.random(Generator.Category.TRINKET));
btnReward.setRect( i*(WIDTH - BTN_GAP) / NUM_TRINKETS - BTN_SIZE, message.top() + message.height() + BTN_GAP, BTN_SIZE, BTN_SIZE );
add( btnReward );

View File

@@ -34,6 +34,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.KindOfWeapon;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfArcana;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfForce;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfFuror;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.ParchmentScrap;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Annoying;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Dazzling;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.curses.Displacing;
@@ -316,10 +317,10 @@ abstract public class Weapon extends KindOfWeapon {
//30% chance to be cursed
//10% chance to be enchanted
float effectRoll = Random.Float();
if (effectRoll < 0.3f) {
if (effectRoll < 0.3f * ParchmentScrap.curseChanceMultiplier()) {
enchant(Enchantment.randomCurse());
cursed = true;
} else if (effectRoll >= 0.9f){
} else if (effectRoll >= 1f - (0.1f * ParchmentScrap.enchantChanceMultiplier())){
enchant();
}

View File

@@ -39,6 +39,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.armor.glyphs.Camouflage;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.SandalsOfNature;
import com.shatteredpixel.shatteredpixeldungeon.items.food.Berry;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.PetrifiedSeed;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.MiningLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
@@ -122,8 +123,18 @@ public class HighGrass {
if (naturalismLevel >= 0) {
// Seed, scales from 1/25 to 1/9
if (Random.Int(25 - (naturalismLevel * 4)) == 0) {
level.drop(Generator.random(Generator.Category.SEED), pos).sprite.drop();
float lootChance = 1/(25f - naturalismLevel*4f);
//absolute max drop rate is ~1/6.5 with footwear of nature, ~1/18 without
lootChance *= PetrifiedSeed.grassLootMultiplier();
if (Random.Float() < lootChance) {
if (Random.Float() < PetrifiedSeed.stoneInsteadOfSeedChance()) {
//TODO do we want to use decks here in some way?
level.drop(Generator.randomUsingDefaults(Generator.Category.STONE), pos).sprite.drop();
} else {
level.drop(Generator.random(Generator.Category.SEED), pos).sprite.drop();
}
}
// Dew, scales from 1/6 to 1/4

View File

@@ -470,8 +470,12 @@ public class ItemSpriteSheet {
private static final int TRINKETS = xy(9, 17); //24 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;
static{
assignItemRect(RAT_SKULL, 16, 11);
assignItemRect(RAT_SKULL, 16, 11);
assignItemRect(PARCHMENT_SCRAP, 10, 14);
assignItemRect(PETRIFIED_SEED, 10, 10);
}
private static final int SCROLLS = xy(1, 19); //16 slots