Add new mobs and new towers

This commit is contained in:
Yohann D'ANELLO 2020-04-10 20:04:59 +02:00
parent 37f0b663fc
commit ca79095672
31 changed files with 273 additions and 78 deletions

View File

@ -69,7 +69,7 @@ Un sprite est une image de taille 16x16, qui contient des informations sur l'end
Un Mob est une classe abstraite contenant des informations abstraites (détaillées plus haut). Un type de mob sera donc Un Mob est une classe abstraite contenant des informations abstraites (détaillées plus haut). Un type de mob sera donc
une classe héritant de `Mob`, telles que `Mob1`, `Mob2` et `MobCancer`. une classe héritant de `Mob`, telles que `Mob1`, `Mob2` et `MobCancer`.
Il en est de même pour les tours, avec `BasicTower`, `NullTower` et `AutoTower`. Il en est de même pour les tours, avec `BasicTower`, `WallTower` et `AutoTower`.
L'intérêt de l'héritage par rapport à un type donné à une classe Mob (paramètres donnés dans une enumération `MobType` L'intérêt de l'héritage par rapport à un type donné à une classe Mob (paramètres donnés dans une enumération `MobType`
par exemple) est de pouvoir mieux personnaliser les fonctions, par exemple en imaginant des dégâts aléatoires. par exemple) est de pouvoir mieux personnaliser les fonctions, par exemple en imaginant des dégâts aléatoires.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 697 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 760 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 731 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -325,16 +325,34 @@
"mob2": [ "mob2": [
[0, 0] [0, 0]
], ],
"mobcancer": [ "mobstrong": [
[0, 0]
],
"mobhealer": [
[0, 0]
],
"mobbreaker": [
[0, 0]
],
"mobspeeder": [
[0, 0] [0, 0]
], ],
"basictower": [ "basictower": [
[0, 0] [0, 0]
], ],
"nulltower": [ "walltower": [
[0, 0] [0, 0]
], ],
"autotower": [ "freezertower": [
[0, 0]
],
"explodertower": [
[0, 0]
],
"upgradetower": [
[0, 0]
],
"lasertower": [
[0, 0] [0, 0]
] ]
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

View File

@ -8,7 +8,7 @@ import fr.ynerant.leveleditor.api.editor.sprites.SpriteRegister
import fr.ynerant.leveleditor.api.editor.{Collision, RawMap} import fr.ynerant.leveleditor.api.editor.{Collision, RawMap}
import fr.ynerant.leveleditor.editor.CollidPanel import fr.ynerant.leveleditor.editor.CollidPanel
import fr.ynerant.leveleditor.game.mobs.Mob import fr.ynerant.leveleditor.game.mobs.Mob
import fr.ynerant.leveleditor.game.towers.{AutoTower, BasicTower, NullTower, Tower} import fr.ynerant.leveleditor.game.towers._
import javax.swing._ import javax.swing._
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
@ -23,8 +23,11 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") {
private var towers = ListBuffer[Tower]() private var towers = ListBuffer[Tower]()
private val pathFinder = PathFinder(this) private val pathFinder = PathFinder(this)
final private var basicTower = null: JRadioButton final private var basicTower = null: JRadioButton
final private var nullTower = null: JRadioButton final private var wallTower = null: JRadioButton
final private var autoTower = null: JRadioButton final private var freezerTower = null: JRadioButton
final private var explodeTower = null: JRadioButton
final private var upgradeTower = null: JRadioButton
final private var laserTower = null: JRadioButton
final private var waveLabel = null: JLabel final private var waveLabel = null: JLabel
final private var nbMobsLabel = null: JLabel final private var nbMobsLabel = null: JLabel
final private var hpLabel = null: JLabel final private var hpLabel = null: JLabel
@ -53,12 +56,21 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") {
basicTower.setSelected(true) basicTower.setSelected(true)
towerSelect.add(basicTower) towerSelect.add(basicTower)
pane.add(basicTower) pane.add(basicTower)
nullTower = new JRadioButton("Tour nulle (" + new NullTower(0, 0).getPrice + " pièces)") wallTower = new JRadioButton("Tour-mur (" + new WallTower(0, 0).getPrice + " pièces)")
towerSelect.add(nullTower) towerSelect.add(wallTower)
pane.add(nullTower) pane.add(wallTower)
autoTower = new JRadioButton("Tour automatique (" + new AutoTower(0, 0).getPrice + " pièces)") freezerTower = new JRadioButton("Tour froide (" + new FreezerTower(0, 0).getPrice + " pièces)")
towerSelect.add(autoTower) towerSelect.add(freezerTower)
pane.add(autoTower) pane.add(freezerTower)
explodeTower = new JRadioButton("Tour explosive (" + new ExploderTower(0, 0).getPrice + " pièces)")
towerSelect.add(explodeTower)
pane.add(explodeTower)
upgradeTower = new JRadioButton("Tour d'amélioration (" + new UpgradeTower(0, 0).getPrice + " pièces)")
towerSelect.add(upgradeTower)
pane.add(upgradeTower)
laserTower = new JRadioButton("Tour laser (" + new LaserTower(0, 0).getPrice + " pièces)")
towerSelect.add(laserTower)
pane.add(laserTower)
waveLabel = new JLabel waveLabel = new JLabel
pane.add(waveLabel) pane.add(waveLabel)
nbMobsLabel = new JLabel nbMobsLabel = new JLabel
@ -92,6 +104,12 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") {
def getMap: RawMap = map def getMap: RawMap = map
def getMobs: ListBuffer[Mob] = mobs
def getTowers: ListBuffer[Tower] = towers
def breakTower(tower: Tower): Unit = towers -= tower
def getPathFinder: PathFinder = pathFinder def getPathFinder: PathFinder = pathFinder
def tick(): Unit = { def tick(): Unit = {
@ -107,11 +125,7 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") {
mobs += mob mobs += mob
} }
} }
towers.foreach(tower => { towers.foreach(tower => tower.shot(this))
tower.filterDetectedMobs(mobs).foreach(mob => {
mob.hit(tower.getDamagePerShot)
})
})
mobs.foreach(mob => { mobs.foreach(mob => {
getMap.getCase(mob.getX, mob.getY).setCollision(Collision.ANY) getMap.getCase(mob.getX, mob.getY).setCollision(Collision.ANY)
mob.tick(this) mob.tick(this)
@ -148,9 +162,11 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") {
val x = event.getX / 32 val x = event.getX / 32
val y = event.getY / 32 val y = event.getY / 32
val tower = if (basicTower.isSelected) new BasicTower(x, y) val tower = if (basicTower.isSelected) new BasicTower(x, y)
else if (nullTower.isSelected) new NullTower(x, y) else if (wallTower.isSelected) new WallTower(x, y)
else if (freezerTower.isSelected) new FreezerTower(x, y)
else if (autoTower.isSelected) new AutoTower(x, y) else if (explodeTower.isSelected) new ExploderTower(x, y)
else if (upgradeTower.isSelected) new UpgradeTower(x, y)
else if (laserTower.isSelected) new LaserTower(x, y)
else null else null
if (tower == null || tower.getPrice > reward) return if (tower == null || tower.getPrice > reward) return
val c = getMap.getCase(x, y) val c = getMap.getCase(x, y)

View File

@ -1,35 +1,48 @@
package fr.ynerant.leveleditor.game.mobs package fr.ynerant.leveleditor.game.mobs
import java.util.Random
import fr.ynerant.leveleditor.api.editor.RawCase import fr.ynerant.leveleditor.api.editor.RawCase
import fr.ynerant.leveleditor.api.editor.sprites.{Sprite, SpriteRegister} import fr.ynerant.leveleditor.api.editor.sprites.{Sprite, SpriteRegister}
import fr.ynerant.leveleditor.game.GameFrame import fr.ynerant.leveleditor.game.GameFrame
import scala.util.Random
object Mob { object Mob {
private val RANDOM = new Random private val RANDOM = new Random
def getRandomMob: Mob = RANDOM.nextInt(3) match { def getRandomMob: Mob = RANDOM.nextInt(6) match {
case 1 => case 0 =>
new Mob1 new Mob1
case 2 => case 1 =>
new Mob2 new Mob2
case _ => case 2 =>
new MobCancer new MobStrong
case 3 =>
new MobHealer
case 4 =>
new MobBreaker
case 5 =>
new MobSpeeder
} }
} }
abstract class Mob() { abstract class Mob() {
private var hp = getMaxHP private var hp = getMaxHP
private var tickRemains = getSlowness private var tickRemains = 0L
private var sprite = null: Sprite private var sprite = null: Sprite
private var x = 0 private var x = 0
private var y = 0 private var y = 0
private var freezeTime = 0
private var speedMultiplier = 1
tickRemains = getSlowness
def getMaxHP: Int def getMaxHP: Int
def getSlowness: Long def _getSlowness: Long
def getSlowness: Long = {
(_getSlowness * Random.between(0.95, 1.05) * (if (freezeTime > 0) 2 else 1) / speedMultiplier).toLong
}
def getReward: Int def getReward: Int
@ -49,6 +62,18 @@ abstract class Mob() {
this.y = y this.y = y
} }
def freeze(time: Int): Unit = {
if (freezeTime == 0)
tickRemains *= 2
freezeTime = time
}
def speedup(multiplier: Int): Unit = speedMultiplier = multiplier
def heal(hp: Int): Unit = {
this.hp = Math.min(hp + 1, getMaxHP)
}
def getHP: Int = hp def getHP: Int = hp
def isDead: Boolean = hp <= 0 def isDead: Boolean = hp <= 0
@ -65,7 +90,13 @@ abstract class Mob() {
false false
} }
/**
* Called each game tick
*/
def tick(game: GameFrame): Unit = { def tick(game: GameFrame): Unit = {
if (freezeTime > 0)
freezeTime -= 1
if (tickRemains > 0) tickRemains -= 1 if (tickRemains > 0) tickRemains -= 1
else { else {
tickRemains = getSlowness tickRemains = getSlowness
@ -75,10 +106,17 @@ abstract class Mob() {
return return
} }
_tick(game)
val newCase: RawCase = game.getPathFinder.nextPos(getX, getY) val newCase: RawCase = game.getPathFinder.nextPos(getX, getY)
move(newCase.getPosX, newCase.getPosY) move(newCase.getPosX, newCase.getPosY)
} }
} }
/**
* Custom mobs override this function to do some custom stuff
*/
def _tick(game: GameFrame): Unit = ()
override def toString: String = "Mob{" + "sprite=" + sprite + ", x=" + x + ", y=" + y + '}' override def toString: String = "Mob{" + "sprite=" + sprite + ", x=" + x + ", y=" + y + '}'
} }

View File

@ -3,7 +3,7 @@ package fr.ynerant.leveleditor.game.mobs
class Mob1 extends Mob { class Mob1 extends Mob {
override def getMaxHP = 2 override def getMaxHP = 2
override def getSlowness = 60 override def _getSlowness = 50
override def getReward = 10 override def getReward = 10

View File

@ -3,7 +3,7 @@ package fr.ynerant.leveleditor.game.mobs
class Mob2 extends Mob { class Mob2 extends Mob {
override def getMaxHP = 6 override def getMaxHP = 6
override def getSlowness = 20 override def _getSlowness = 20
override def getReward = 20 override def getReward = 20

View File

@ -0,0 +1,18 @@
package fr.ynerant.leveleditor.game.mobs
import fr.ynerant.leveleditor.game.GameFrame
class MobBreaker extends Mob {
override def getMaxHP = 40
override def _getSlowness = 120
override def getReward = 70
override def getName = "mobhealer"
override def _tick(game: GameFrame): Unit = {
game.getTowers.filter(tower => Math.abs(tower.getX - getX) + Math.abs(tower.getY - getY) <= 1).foreach(tower => game.breakTower(tower))
game.getPathFinder.invalidate
}
}

View File

@ -0,0 +1,17 @@
package fr.ynerant.leveleditor.game.mobs
import fr.ynerant.leveleditor.game.GameFrame
class MobHealer extends Mob {
override def getMaxHP = 20
override def _getSlowness = 60
override def getReward = 20
override def getName = "mobhealer"
override def _tick(game: GameFrame): Unit = {
game.getMobs.filter(mob => Math.pow(mob.getX - getX, 2) + Math.pow(mob.getY - getY, 2) <= 9).foreach(mob => mob.heal(1))
}
}

View File

@ -0,0 +1,17 @@
package fr.ynerant.leveleditor.game.mobs
import fr.ynerant.leveleditor.game.GameFrame
class MobSpeeder extends Mob {
override def getMaxHP = 25
override def _getSlowness = 60
override def getReward = 30
override def getName = "mobspeeder"
override def _tick(game: GameFrame): Unit = {
game.getMobs.filter(mob => Math.pow(mob.getX - getX, 2) + Math.pow(mob.getY - getY, 2) <= 9).foreach(mob => mob.speedup(3))
}
}

View File

@ -1,11 +1,11 @@
package fr.ynerant.leveleditor.game.mobs package fr.ynerant.leveleditor.game.mobs
class MobCancer extends Mob { class MobStrong extends Mob {
override def getMaxHP = 50 override def getMaxHP = 50
override def getSlowness = 100 override def _getSlowness = 100
override def getReward = 100 override def getReward = 100
override def getName = "mobcancer" override def getName = "mobstrong"
} }

View File

@ -1,16 +0,0 @@
package fr.ynerant.leveleditor.game.towers
import fr.ynerant.leveleditor.game.mobs.Mob
class AutoTower(override val x: Int, override val y: Int) extends Tower(x, y) {
override def getName = "autotower"
override def getDamagePerShot = 20
override def getPeriod = 10
override def getPrice = 142
override private[towers] def _filterDetectedMobs(mobs: Iterable[Mob]) = mobs
}

View File

@ -1,18 +1,23 @@
package fr.ynerant.leveleditor.game.towers package fr.ynerant.leveleditor.game.towers
import fr.ynerant.leveleditor.game.mobs.Mob import fr.ynerant.leveleditor.game.GameFrame
import scala.util.Random
class BasicTower(override val x: Int, override val y: Int) extends Tower(x, y) { class BasicTower(override val x: Int, override val y: Int) extends Tower(x, y) {
override def getName = "basictower" override def getName = "basictower"
override def getDamagePerShot = 1 override def getDamagePerShot = if (isUpgraded) 3 else 1
override def getPeriod = 5 override def getPeriod = 5
override def getPrice = 10 override def getPrice = 10
override private[towers] def _filterDetectedMobs(mobs: Iterable[Mob]) = { override private[towers] def _shot(game: GameFrame): Unit = {
mobs.filter(mob => (mob.getX == getX || mob.getY == getY) && Math.abs(mob.getX - getX) <= 3 && Math.abs(mob.getY - getY) <= 3) var l = game.getMobs.filter(mob => Math.pow(mob.getX - getX, 2) + Math.pow(mob.getY - getY, 2) <= 9).toList
l = Random.shuffle(l)
if (l.nonEmpty)
l.head.hit(getDamagePerShot)
} }
} }

View File

@ -0,0 +1,26 @@
package fr.ynerant.leveleditor.game.towers
import fr.ynerant.leveleditor.game.GameFrame
import scala.util.Random
class ExploderTower(override val x: Int, override val y: Int) extends Tower(x, y) {
override def getName = "explodertower"
override def getDamagePerShot = if (isUpgraded) 7 else 3
override def getPeriod = 20
override def getPrice = 70
override private[towers] def _shot(game: GameFrame): Unit = {
var l = game.getMobs.filter(mob => Math.pow(mob.getX - getX, 2) + Math.pow(mob.getY - getY, 2) <= 25).toList
l = Random.shuffle(l)
if (l.nonEmpty) {
val target = l.head
target.hit(getDamagePerShot)
game.getMobs.filter(mob => Math.pow(mob.getX - target.getX, 2) + Math.pow(mob.getX - target.getX, 2) <= 9)
.foreach(mob => mob.hit(getDamagePerShot))
}
}
}

View File

@ -0,0 +1,17 @@
package fr.ynerant.leveleditor.game.towers;
import fr.ynerant.leveleditor.game.GameFrame
class FreezerTower(override val x: Int, override val y: Int) extends Tower(x, y) {
override def getName = "freezertower"
override def getDamagePerShot = if (isUpgraded) 1 else 0
override def getPeriod = 10
override def getPrice = 40
override private[towers] def _shot(game: GameFrame): Unit = {
game.getMobs.filter(mob => Math.abs(mob.getX - getX) <= 3 && Math.abs(mob.getY - getY) <= 3).foreach(mob => mob.freeze(if (isUpgraded) 100 else 40))
}
}

View File

@ -0,0 +1,18 @@
package fr.ynerant.leveleditor.game.towers
import fr.ynerant.leveleditor.game.GameFrame
class LaserTower(override val x: Int, override val y: Int) extends Tower(x, y) {
override def getName = "lasertower"
override def getDamagePerShot = 3
override def getPeriod = 40
override def getPrice = 80
override private[towers] def _shot(game: GameFrame): Unit = {
game.getMobs.filter(mob => mob.getX == getX || mob.getY == getY).foreach(mob => mob.hit(getDamagePerShot))
}
}

View File

@ -1,16 +0,0 @@
package fr.ynerant.leveleditor.game.towers
import fr.ynerant.leveleditor.game.mobs.Mob
class NullTower(override val x: Int, override val y: Int) extends Tower(x, y) {
override def getName = "nulltower"
override def getDamagePerShot: Int = Integer.MAX_VALUE
override def getPeriod = 1
override def getPrice = 5
override private[towers] def _filterDetectedMobs(mobs: Iterable[Mob]) = Nil
}

View File

@ -3,8 +3,7 @@ package fr.ynerant.leveleditor.game.towers
import java.util.Random import java.util.Random
import fr.ynerant.leveleditor.api.editor.sprites.{Sprite, SpriteRegister} import fr.ynerant.leveleditor.api.editor.sprites.{Sprite, SpriteRegister}
import fr.ynerant.leveleditor.game.mobs.Mob import fr.ynerant.leveleditor.game.GameFrame
object Tower { object Tower {
private val RANDOM = new Random private val RANDOM = new Random
@ -13,6 +12,7 @@ object Tower {
abstract class Tower(val x: Int, val y: Int) { abstract class Tower(val x: Int, val y: Int) {
final private val sprite = SpriteRegister.getCategory(getName).getSprites.head final private val sprite = SpriteRegister.getCategory(getName).getSprites.head
private var remainingTicks = 0L private var remainingTicks = 0L
private var upgraded = false
def getSprite: Sprite = sprite def getSprite: Sprite = sprite
@ -24,16 +24,18 @@ abstract class Tower(val x: Int, val y: Int) {
def getPrice: Int def getPrice: Int
def filterDetectedMobs(mobs: Iterable[Mob]): Iterable[Mob] = if (remainingTicks > 0) { def upgrade: Unit = upgraded = true
def isUpgraded: Boolean = upgraded
def shot(game: GameFrame): Unit = if (remainingTicks > 0)
remainingTicks -= 1 remainingTicks -= 1
Nil
}
else { else {
remainingTicks = getPeriod remainingTicks = getPeriod
_filterDetectedMobs(mobs) _shot(game)
} }
private[towers] def _filterDetectedMobs(mobs: Iterable[Mob]): Iterable[Mob] private[towers] def _shot(game: GameFrame): Unit
def getX: Int = x def getX: Int = x

View File

@ -0,0 +1,19 @@
package fr.ynerant.leveleditor.game.towers
import fr.ynerant.leveleditor.game.GameFrame
import scala.util.Random
class UpgradeTower(override val x: Int, override val y: Int) extends Tower(x, y) {
override def getName = "upgradetower"
override def getDamagePerShot = if (isUpgraded) 1 else 0
override def getPeriod = 60
override def getPrice = 65
override private[towers] def _shot(game: GameFrame): Unit = {
game.getTowers.filter(tower => Math.pow(tower.getX - getX, 2) + Math.pow(tower.getY - getY, 2) <= 25 && tower != this).foreach(tower => tower.upgrade)
}
}

View File

@ -0,0 +1,16 @@
package fr.ynerant.leveleditor.game.towers
import fr.ynerant.leveleditor.game.GameFrame
class WallTower(override val x: Int, override val y: Int) extends Tower(x, y) {
override def getName = "walltower"
override def getDamagePerShot: Int = Integer.MAX_VALUE
override def getPeriod = 1
override def getPrice = 5
override private[towers] def _shot(game: GameFrame): Unit = ()
}