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
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`
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": [
[0, 0]
],
"mobcancer": [
"mobstrong": [
[0, 0]
],
"mobhealer": [
[0, 0]
],
"mobbreaker": [
[0, 0]
],
"mobspeeder": [
[0, 0]
],
"basictower": [
[0, 0]
],
"nulltower": [
"walltower": [
[0, 0]
],
"autotower": [
"freezertower": [
[0, 0]
],
"explodertower": [
[0, 0]
],
"upgradetower": [
[0, 0]
],
"lasertower": [
[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.editor.CollidPanel
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 scala.collection.mutable.ListBuffer
@ -23,8 +23,11 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") {
private var towers = ListBuffer[Tower]()
private val pathFinder = PathFinder(this)
final private var basicTower = null: JRadioButton
final private var nullTower = null: JRadioButton
final private var autoTower = null: JRadioButton
final private var wallTower = 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 nbMobsLabel = null: JLabel
final private var hpLabel = null: JLabel
@ -53,12 +56,21 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") {
basicTower.setSelected(true)
towerSelect.add(basicTower)
pane.add(basicTower)
nullTower = new JRadioButton("Tour nulle (" + new NullTower(0, 0).getPrice + " pièces)")
towerSelect.add(nullTower)
pane.add(nullTower)
autoTower = new JRadioButton("Tour automatique (" + new AutoTower(0, 0).getPrice + " pièces)")
towerSelect.add(autoTower)
pane.add(autoTower)
wallTower = new JRadioButton("Tour-mur (" + new WallTower(0, 0).getPrice + " pièces)")
towerSelect.add(wallTower)
pane.add(wallTower)
freezerTower = new JRadioButton("Tour froide (" + new FreezerTower(0, 0).getPrice + " pièces)")
towerSelect.add(freezerTower)
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
pane.add(waveLabel)
nbMobsLabel = new JLabel
@ -92,6 +104,12 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") {
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 tick(): Unit = {
@ -107,11 +125,7 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") {
mobs += mob
}
}
towers.foreach(tower => {
tower.filterDetectedMobs(mobs).foreach(mob => {
mob.hit(tower.getDamagePerShot)
})
})
towers.foreach(tower => tower.shot(this))
mobs.foreach(mob => {
getMap.getCase(mob.getX, mob.getY).setCollision(Collision.ANY)
mob.tick(this)
@ -148,9 +162,11 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") {
val x = event.getX / 32
val y = event.getY / 32
val tower = if (basicTower.isSelected) new BasicTower(x, y)
else if (nullTower.isSelected) new NullTower(x, y)
else if (autoTower.isSelected) new AutoTower(x, y)
else if (wallTower.isSelected) new WallTower(x, y)
else if (freezerTower.isSelected) new FreezerTower(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
if (tower == null || tower.getPrice > reward) return
val c = getMap.getCase(x, y)

View File

@ -1,35 +1,48 @@
package fr.ynerant.leveleditor.game.mobs
import java.util.Random
import fr.ynerant.leveleditor.api.editor.RawCase
import fr.ynerant.leveleditor.api.editor.sprites.{Sprite, SpriteRegister}
import fr.ynerant.leveleditor.game.GameFrame
import scala.util.Random
object Mob {
private val RANDOM = new Random
def getRandomMob: Mob = RANDOM.nextInt(3) match {
case 1 =>
def getRandomMob: Mob = RANDOM.nextInt(6) match {
case 0 =>
new Mob1
case 2 =>
case 1 =>
new Mob2
case _ =>
new MobCancer
case 2 =>
new MobStrong
case 3 =>
new MobHealer
case 4 =>
new MobBreaker
case 5 =>
new MobSpeeder
}
}
abstract class Mob() {
private var hp = getMaxHP
private var tickRemains = getSlowness
private var tickRemains = 0L
private var sprite = null: Sprite
private var x = 0
private var y = 0
private var freezeTime = 0
private var speedMultiplier = 1
tickRemains = getSlowness
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
@ -49,6 +62,18 @@ abstract class Mob() {
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 isDead: Boolean = hp <= 0
@ -65,7 +90,13 @@ abstract class Mob() {
false
}
/**
* Called each game tick
*/
def tick(game: GameFrame): Unit = {
if (freezeTime > 0)
freezeTime -= 1
if (tickRemains > 0) tickRemains -= 1
else {
tickRemains = getSlowness
@ -75,10 +106,17 @@ abstract class Mob() {
return
}
_tick(game)
val newCase: RawCase = game.getPathFinder.nextPos(getX, getY)
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 + '}'
}

View File

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

View File

@ -3,7 +3,7 @@ package fr.ynerant.leveleditor.game.mobs
class Mob2 extends Mob {
override def getMaxHP = 6
override def getSlowness = 20
override def _getSlowness = 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
class MobCancer extends Mob {
class MobStrong extends Mob {
override def getMaxHP = 50
override def getSlowness = 100
override def _getSlowness = 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
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) {
override def getName = "basictower"
override def getDamagePerShot = 1
override def getDamagePerShot = if (isUpgraded) 3 else 1
override def getPeriod = 5
override def getPrice = 10
override private[towers] def _filterDetectedMobs(mobs: Iterable[Mob]) = {
mobs.filter(mob => (mob.getX == getX || mob.getY == getY) && Math.abs(mob.getX - getX) <= 3 && Math.abs(mob.getY - getY) <= 3)
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) <= 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 fr.ynerant.leveleditor.api.editor.sprites.{Sprite, SpriteRegister}
import fr.ynerant.leveleditor.game.mobs.Mob
import fr.ynerant.leveleditor.game.GameFrame
object Tower {
private val RANDOM = new Random
@ -13,6 +12,7 @@ object Tower {
abstract class Tower(val x: Int, val y: Int) {
final private val sprite = SpriteRegister.getCategory(getName).getSprites.head
private var remainingTicks = 0L
private var upgraded = false
def getSprite: Sprite = sprite
@ -24,16 +24,18 @@ abstract class Tower(val x: Int, val y: 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
Nil
}
else {
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

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 = ()
}