diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 224246631..53c517e01 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -337,6 +337,10 @@ items.artifacts.etherealchains.desc_cursed=The cursed chains are locked to your items.artifacts.etherealchains.desc_equipped=The chains rest around your side, slowly siphoning the spiritual energy of those you defeat. Each charge is a link in the chain, which will extend out exactly one tile. items.artifacts.etherealchains$chainsrecharge.levelup=Your chains grow stronger! +items.artifacts.holytome.name=holy tome +items.artifacts.holytome.ac_cast=CAST +items.artifacts.holytome.desc=TODO + items.artifacts.hornofplenty.name=horn of plenty items.artifacts.hornofplenty.ac_snack=SNACK items.artifacts.hornofplenty.ac_eat=EAT @@ -1741,6 +1745,13 @@ items.weapon.melee.crossbow.upgrade_ability_stat_name=Ability Boost items.weapon.melee.crossbow$chargedshot.name=charged items.weapon.melee.crossbow$chargedshot.desc=The Duelist is focusing power into her crossbow. The next attack she makes with it will always hit and apply one of three effects:\n- Melee attacks will knock enemies a few tiles away.\n- Untipped darts will deal bonus damage.\n- Tipped darts will gain extra uses and apply their effect in a 7x7 area. Positive dart effects will only affect allies, and harmful effects will only apply to enemies. The Duelist cannot use this ability to apply positive dart effects to herself. +items.weapon.melee.cudgel.name=cudgel +items.weapon.melee.cudgel.stats_desc=This is a rather accurate weapon. +items.weapon.melee.cudgel.ability_name=heavy blow +items.weapon.melee.cudgel.typical_ability_desc=The Duelist can perform a _heavy blow_ with a cudgel. This concentrated attack typically deals _%1$d-%2$d damage_ if it surprised the enemy, and dazes them for 5 turns which reduces accuracy and evasion by 50%%. Heavy blow always hits but deals regular damage if it does not surprise the enemy. +items.weapon.melee.cudgel.ability_desc=The Duelist can perform a _heavy blow_ with a cudgel. This concentrated attack deals _%1$d-%2$d damage_ if it surprised the enemy, and dazes them for 5 turns which reduces accuracy and evasion by 50%%. Heavy blow always hits but deals regular damage if it does not surprise the enemy. +items.weapon.melee.cudgel.desc=A small brass mace, made more as a self-defense tool for nobles than as an adventuring weapon. + items.weapon.melee.dagger.name=dagger items.weapon.melee.dagger.stats_desc=This weapon is stronger against unaware enemies. items.weapon.melee.dagger.ability_name=sneak diff --git a/core/src/main/assets/sprites/items.png b/core/src/main/assets/sprites/items.png index 2c3e45186..ca8ff9d2b 100644 Binary files a/core/src/main/assets/sprites/items.png and b/core/src/main/assets/sprites/items.png differ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java index 332932b1e..f56017397 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java @@ -48,6 +48,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.Waterskin; import com.shatteredpixel.shatteredpixeldungeon.items.armor.ClothArmor; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.CloakOfShadows; +import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome; import com.shatteredpixel.shatteredpixeldungeon.items.bags.VelvetPouch; import com.shatteredpixel.shatteredpixeldungeon.items.food.Food; import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfHealing; @@ -65,6 +66,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfRemoveCurs import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade; import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfMagicMissile; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Cudgel; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Dagger; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Gloves; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.MagesStaff; @@ -241,11 +243,14 @@ public enum HeroClass { private static void initCleric( Hero hero ) { - //TODO Cudgel - (hero.belongings.weapon = new Dagger()).identify(); + (hero.belongings.weapon = new Cudgel()).identify(); hero.belongings.weapon.activate(hero); - //TODO Spellbook + HolyTome tome = new HolyTome(); + (hero.belongings.artifact = tome).identify(); + hero.belongings.artifact.activate( hero ); + + Dungeon.quickslot.setSlot(0, tome); new PotionOfPurity().identify(); new ScrollOfRemoveCurse().identify(); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java index 4e3c5909d..949329a2c 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/Generator.java @@ -39,6 +39,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.ChaliceOfBlood; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.CloakOfShadows; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.EtherealChains; +import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HornOfPlenty; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.MasterThievesArmband; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.SandalsOfNature; @@ -141,6 +142,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfWarding; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.AssassinsBlade; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.BattleAxe; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Crossbow; +import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Cudgel; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Dagger; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Dirk; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Flail; @@ -414,9 +416,10 @@ public class Generator { MagesStaff.class, Dagger.class, Gloves.class, - Rapier.class + Rapier.class, + Cudgel.class, }; - WEP_T1.defaultProbs = new float[]{ 2, 0, 2, 2, 2 }; + WEP_T1.defaultProbs = new float[]{ 2, 0, 2, 2, 2, 2 }; WEP_T1.probs = WEP_T1.defaultProbs.clone(); WEP_T2.classes = new Class[]{ @@ -555,6 +558,7 @@ public class Generator { CloakOfShadows.class, DriedRose.class, EtherealChains.class, + HolyTome.class, HornOfPlenty.class, MasterThievesArmband.class, SandalsOfNature.class, @@ -562,7 +566,7 @@ public class Generator { TimekeepersHourglass.class, UnstableSpellbook.class }; - ARTIFACT.defaultProbs = new float[]{ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 }; + ARTIFACT.defaultProbs = new float[]{ 1, 1, 0, 1, 1, 0, 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 diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/HolyTome.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/HolyTome.java new file mode 100644 index 000000000..bffdea276 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/HolyTome.java @@ -0,0 +1,81 @@ +/* + * 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 + */ + +package com.shatteredpixel.shatteredpixeldungeon.items.artifacts; + +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.MagicImmune; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; + +import java.util.ArrayList; + +public class HolyTome extends Artifact { + + { + image = ItemSpriteSheet.ARTIFACT_TOME; + + exp = 0; + levelCap = 10; + + charge = Math.min(level()+3, 10); + partialCharge = 0; + chargeCap = Math.min(level()+3, 10); + + defaultAction = AC_CAST; + + unique = true; + bones = false; + } + + public static final String AC_CAST = "CAST"; + + @Override + public ArrayList actions( Hero hero ) { + ArrayList actions = super.actions( hero ); + if (isEquipped( hero ) + && !cursed + && hero.buff(MagicImmune.class) == null + && charge > 0) { + actions.add(AC_CAST); + } + return actions; + } + + //levelling: + //starts with 3 charges at +0, reaches 10 charges at +7. +8,9,10 slightly increases charge speed + //levels up based on use, probably with a very similar target level system to the Cloak + + //how does the UI for actually using it work though? + + @Override + protected ArtifactBuff passiveBuff() { + return new tomeRecharge(); + } + + public class tomeRecharge extends ArtifactBuff{ + + public void gainCharge(float levelPortion) { + //TODO + } + + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Cudgel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Cudgel.java new file mode 100644 index 000000000..801fa7551 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Cudgel.java @@ -0,0 +1,75 @@ +/* + * 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 + */ + +package com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee; + +import com.shatteredpixel.shatteredpixeldungeon.Assets; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet; + +public class Cudgel extends MeleeWeapon { + + { + image = ItemSpriteSheet.CUDGEL; + hitSound = Assets.Sounds.HIT_CRUSH; + hitSoundPitch = 1.2f; + + tier = 1; + ACC = 1.40f; //40% boost to accuracy + + bones = false; + } + + @Override + public int max(int lvl) { + return 4*(tier+1) + //8 base, down from 10 + lvl*(tier+1); //scaling unchanged + } + + @Override + public String targetingPrompt() { + return Messages.get(this, "prompt"); + } + + @Override + protected void duelistAbility(Hero hero, Integer target) { + //+(3+1.5*lvl) damage, roughly +67% base dmg, +100% scaling + int dmgBoost = augment.damageFactor(3 + Math.round(1.5f*buffedLvl())); + Mace.heavyBlowAbility(hero, target, 1, dmgBoost, this); + } + + @Override + public String abilityInfo() { + int dmgBoost = levelKnown ? 3 + Math.round(1.5f*buffedLvl()) : 3; + if (levelKnown){ + return Messages.get(this, "ability_desc", augment.damageFactor(min()+dmgBoost), augment.damageFactor(max()+dmgBoost)); + } else { + return Messages.get(this, "typical_ability_desc", min(0)+dmgBoost, max(0)+dmgBoost); + } + } + + public String upgradeAbilityStat(int level){ + int dmgBoost = 3 + Math.round(1.5f*level); + return augment.damageFactor(min(level)+dmgBoost) + "-" + augment.damageFactor(max(level)+dmgBoost); + } + +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Mace.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Mace.java index 08564ba8e..e9aaf72b9 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Mace.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/weapon/melee/Mace.java @@ -61,7 +61,7 @@ public class Mace extends MeleeWeapon { @Override protected void duelistAbility(Hero hero, Integer target) { - //+(4+1.5*lvl) damage, roughly +55% base dmg, +60% scaling + //+(5+1.5*lvl) damage, roughly +55% base dmg, +60% scaling int dmgBoost = augment.damageFactor(5 + Math.round(1.5f*buffedLvl())); Mace.heavyBlowAbility(hero, target, 1, dmgBoost, this); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java index 6273ea587..ebb776d54 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ItemSpriteSheet.java @@ -213,13 +213,14 @@ public class ItemSpriteSheet { private static final int WEP_TIER1 = xy(1, 7); //8 slots public static final int WORN_SHORTSWORD = WEP_TIER1+0; - public static final int CUDGEL = WEP_TIER1+1; + public static final int CUDGEL = WEP_TIER1+1; //TODO CLERIC better sprite public static final int GLOVES = WEP_TIER1+2; public static final int RAPIER = WEP_TIER1+3; public static final int DAGGER = WEP_TIER1+4; public static final int MAGES_STAFF = WEP_TIER1+5; static{ assignItemRect(WORN_SHORTSWORD, 13, 13); + assignItemRect(CUDGEL, 16, 16); assignItemRect(GLOVES, 12, 16); assignItemRect(RAPIER, 13, 14); assignItemRect(DAGGER, 12, 13); @@ -449,6 +450,7 @@ public class ItemSpriteSheet { public static final int ARTIFACT_ROSE1 = ARTIFACTS+20; public static final int ARTIFACT_ROSE2 = ARTIFACTS+21; public static final int ARTIFACT_ROSE3 = ARTIFACTS+22; + public static final int ARTIFACT_TOME = ARTIFACTS+23; //TODO CLERIC finalize sprite static{ assignItemRect(ARTIFACT_CLOAK, 9, 15); assignItemRect(ARTIFACT_ARMBAND, 16, 13); @@ -473,6 +475,7 @@ public class ItemSpriteSheet { assignItemRect(ARTIFACT_ROSE1, 14, 14); assignItemRect(ARTIFACT_ROSE2, 14, 14); assignItemRect(ARTIFACT_ROSE3, 14, 14); + assignItemRect(ARTIFACT_TOME, 14, 16); } private static final int TRINKETS = xy(9, 17); //24 slots diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Icons.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Icons.java index 2e41e49be..4e43accb8 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Icons.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/Icons.java @@ -460,8 +460,8 @@ public enum Icons { return new ItemSprite(ItemSpriteSheet.SPIRIT_BOW); case DUELIST: return new ItemSprite(ItemSpriteSheet.RAPIER); - case CLERIC: //TODO CLERIC class sprite - return new ItemSprite(ItemSpriteSheet.MASTERY); + case CLERIC: + return new ItemSprite(ItemSpriteSheet.ARTIFACT_TOME); default: return null; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndHeroInfo.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndHeroInfo.java index afe7097e4..ce90e58ac 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndHeroInfo.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/windows/WndHeroInfo.java @@ -73,8 +73,8 @@ public class WndHeroInfo extends WndTabbed { case DUELIST: tabIcon = new ItemSprite(ItemSpriteSheet.RAPIER, null); break; - case CLERIC: //TODO CLERIC - tabIcon = new ItemSprite(ItemSpriteSheet.MASTERY, null); + case CLERIC: + tabIcon = new ItemSprite(ItemSpriteSheet.ARTIFACT_TOME, null); break; } @@ -201,10 +201,10 @@ public class WndHeroInfo extends WndTabbed { new ItemSprite(ItemSpriteSheet.THROWING_SPIKE), new ItemSprite(ItemSpriteSheet.SCROLL_ISAZ)}; break; - case CLERIC: //TODO CLERIC - icons = new Image[]{ new ItemSprite(ItemSpriteSheet.MASTERY), + case CLERIC: + icons = new Image[]{ new ItemSprite(ItemSpriteSheet.ARTIFACT_TOME), Icons.TALENT.get(), - new ItemSprite(ItemSpriteSheet.MACE), + new ItemSprite(ItemSpriteSheet.CUDGEL), new ItemSprite(ItemSpriteSheet.SCROLL_ISAZ)}; break; } @@ -228,6 +228,7 @@ public class WndHeroInfo extends WndTabbed { icons[i].x = (20-icons[i].width())/2; icons[i].y = info[i].top() + (info[i].height() - icons[i].height())/2; + PixelScene.align(icons[i]); pos = info[i].bottom() + 4*MARGIN; }