v2.3.0: added some very basic early gnoll sapper and guard interactions

This commit is contained in:
Evan Debenham
2023-11-02 17:07:30 -04:00
parent 9bc886dd8a
commit c5908c1849
6 changed files with 270 additions and 1 deletions

View File

@@ -1208,8 +1208,15 @@ actors.mobs.ghoul.desc=As dwarven society slowly began to collapse, and the curr
actors.mobs.gnoll.name=gnoll scout
actors.mobs.gnoll.desc=Gnolls are hyena-like humanoids. They dwell in sewers and dungeons, venturing up to raid the surface from time to time. Gnoll scouts are regular members of their pack, they are not as strong as brutes and not as intelligent as shamans.
actors.mobs.gnollgeomancer.name=gnoll geomancer
actors.mobs.gnollgeomancer.desc=TODO
actors.mobs.gnollguard.name=gnoll guard
actors.mobs.gnollguard.desc=A large and tough looking gnoll wielding a spear and a shield. These guards are likely brutes in training, roped into helping protect the mine from encroaching wildlife.\n\nThe gnoll guard is strong enough to wield the spear in one hand, but can't use it very well. It will need fairly open space to attack at a distance, and will do notably less damage to you if you get up close.
actors.mobs.gnollguard.desc=A large and tough looking gnoll wielding a spear and a shield, but no helmet. These guards are likely brutes in training, roped into helping protect the mine from encroaching wildlife.\n\nThe gnoll guard is strong enough to wield the spear in one hand, but can't use it very well. It will need fairly open space to attack at a distance, and will do notably less damage to you if you get up close.
actors.mobs.gnollguard.desc_armor=A nearby sapper is holding a device that is granting this guard earthen armor, heavily reducing the damage it takes.
actors.mobs.gnollsapper.name=gnoll sapper
actors.mobs.gnollsapper.desc=TODO
actors.mobs.gnolltrickster.name=gnoll trickster
actors.mobs.gnolltrickster.desc=A strange looking creature, even by gnoll standards. It hunches forward with a wicked grin, almost cradling the satchel hanging over its shoulder. Its eyes are wide with a strange mix of fear and excitement.\n\nThere is a large collection of poorly made darts in its satchel, they all seem to be tipped with various harmful substances.

View File

@@ -22,10 +22,13 @@
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.melee.Spear;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.sprites.GnollGuardSprite;
import com.watabou.utils.Bundle;
import com.watabou.utils.Random;
public class GnollGuard extends Mob {
@@ -41,6 +44,38 @@ public class GnollGuard extends Mob {
loot = Spear.class;
lootChance = 0.1f;
WANDERING = new Wandering();
}
private int sapperID = -1;
public void linkSapper( GnollSapper sapper){
this.sapperID = sapper.id();
if (sprite instanceof GnollGuardSprite){
((GnollGuardSprite) sprite).setupArmor();
}
}
public boolean hasSapper(){
return sapperID != -1
&& Actor.findById(sapperID) instanceof GnollSapper
&& ((GnollSapper)Actor.findById(sapperID)).isAlive();
}
public void loseSapper(){
if (sapperID != -1){
sapperID = -1;
if (sprite instanceof GnollGuardSprite){
((GnollGuardSprite) sprite).loseArmor();
}
}
}
@Override
public void damage(int dmg, Object src) {
if (hasSapper()) dmg /= 4;
super.damage(dmg, src);
}
@Override
@@ -70,4 +105,38 @@ public class GnollGuard extends Mob {
&& new Ballistica( enemy.pos, pos, Ballistica.PROJECTILE).collisionPos == pos;
}
@Override
public String description() {
if (hasSapper()){
return super.description() + "\n\n" + Messages.get(this, "desc_armor");
} else {
return super.description();
}
}
private static final String SAPPER_ID = "sapper_id";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put(SAPPER_ID, sapperID);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
sapperID = bundle.getInt(SAPPER_ID);
}
public class Wandering extends Mob.Wandering {
@Override
protected int randomDestination() {
if (hasSapper()){
return ((GnollSapper)Actor.findById(sapperID)).pos;
} else {
return super.randomDestination();
}
}
}
}

View File

@@ -0,0 +1,102 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2023 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.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.sprites.GnollSapperSprite;
import com.watabou.utils.Bundle;
import java.util.ArrayList;
public class GnollSapper extends Mob {
{
spriteClass = GnollSapperSprite.class;
HP = HT = 35;
defenseSkill = 15;
EXP = 10;
maxLvl = -2;
properties.add(Property.MINIBOSS);
SLEEPING = new Sleeping();
state = SLEEPING;
//TODO wandering and hunting too. Partly for abilities, but also for other logic
}
private ArrayList<Integer> guardIDs = new ArrayList<>();
public void linkGuard(GnollGuard g){
guardIDs.add(g.id());
g.linkSapper(this);
}
@Override
public void die(Object cause) {
super.die(cause);
for (Integer g : guardIDs){
if (Actor.findById(g) instanceof GnollGuard){
((GnollGuard) Actor.findById(g)).loseSapper();
}
}
}
private static final String GUARD_IDS = "guard_ids";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
int[] toStore = new int[guardIDs.size()];
for (int i = 0; i < toStore.length; i++){
toStore[i] = guardIDs.get(i);
}
bundle.put(GUARD_IDS, toStore);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
guardIDs = new ArrayList<>();
for (int g : bundle.getIntArray(GUARD_IDS)){
guardIDs.add(g);
}
}
public class Sleeping extends Mob.Sleeping {
@Override
public boolean act(boolean enemyInFOV, boolean justAlerted) {
if (guardIDs.size() < 2){
for (Char ch : Actor.chars()){
if (fieldOfView[ch.pos] && ch instanceof GnollGuard && !guardIDs.contains(ch.id())){
linkGuard((GnollGuard) ch);
break;
}
}
}
return super.act(enemyInFOV, justAlerted);
}
}
}

View File

@@ -36,6 +36,13 @@ public class EarthParticle extends PixelParticle {
}
};
public static final Emitter.Factory SMALL = new Factory() {
@Override
public void emit( Emitter emitter, int index, float x, float y ) {
((EarthParticle)emitter.recycle( EarthParticle.class )).resetSmall( x, y );
}
};
public static final Emitter.Factory FALLING = new Factory() {
@Override
public void emit( Emitter emitter, int index, float x, float y ) {
@@ -60,6 +67,13 @@ public class EarthParticle extends PixelParticle {
size = 16;
}
public void resetSmall( float x, float y ) {
reset(x, y);
left = lifespan = 1f;
size = 8;
}
public void resetFalling( float x, float y ) {
reset(x, y);

View File

@@ -22,6 +22,8 @@
package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.quest;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.CrystalGuardian;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.GnollGuard;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.GnollSapper;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Blacksmith;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
@@ -145,6 +147,21 @@ public class MineLargeRoom extends CaveRoom {
}
}
//TODO refine this with barricades
Point p = random(5);
GnollSapper s = new GnollSapper();
s.pos = level.pointToCell(p);
level.mobs.add(s);
Painter.set(level, p, Terrain.EMPTY);
p = random(4);
GnollGuard g = new GnollGuard();
g.pos = level.pointToCell(p);
level.mobs.add(g);
Painter.set(level, p, Terrain.EMPTY);
s.linkGuard(g);
} else {
Painter.fillEllipse(level, this, 3, Terrain.EMPTY);
}

View File

@@ -22,10 +22,16 @@
package com.shatteredpixel.shatteredpixeldungeon.sprites;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.GnollGuard;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.EarthParticle;
import com.watabou.noosa.TextureFilm;
import com.watabou.noosa.particles.Emitter;
public class GnollGuardSprite extends MobSprite {
private Emitter earthArmor;
public GnollGuardSprite() {
super();
@@ -48,4 +54,58 @@ public class GnollGuardSprite extends MobSprite {
play( idle );
}
@Override
public void link( Char ch ) {
super.link( ch );
if (ch instanceof GnollGuard && ((GnollGuard) ch).hasSapper()){
setupArmor();
}
}
public void setupArmor(){
if (earthArmor == null) {
earthArmor = emitter();
earthArmor.fillTarget = false;
earthArmor.y = height()/2f;
earthArmor.x = 2;
earthArmor.width = width()-4;
earthArmor.height = height() - 10f;
earthArmor.pour(EarthParticle.SMALL, 0.15f);
}
}
public void loseArmor(){
if (earthArmor != null){
earthArmor.on = false;
earthArmor = null;
}
}
@Override
public void update() {
super.update();
if (earthArmor != null){
earthArmor.visible = visible;
}
}
@Override
public void die() {
super.die();
if (earthArmor != null){
earthArmor.on = false;
}
}
@Override
public void kill() {
super.kill();
if (earthArmor != null){
earthArmor.killAndErase();
}
}
}