diff --git a/.travis.yml b/.travis.yml index 523725d..cda070f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ sudo: false language: scala jdk: openjdk11 scala: - - 2.11.6 + - 2.13.1 script: sbt test site notifications: email: diff --git a/build.sbt b/build.sbt index 57c7102..564630a 100644 --- a/build.sbt +++ b/build.sbt @@ -6,12 +6,13 @@ name := "Keter" version := "0.0.1" -scalaVersion := "2.11.12" +scalaVersion := "2.13.1" libraryDependencies ++= Seq( - "com.lihaoyi" %%% "utest" % "0.3.1", - "org.scala-js" %%% "scalajs-dom" % "0.8.0", - "org.webjars" % "rot.js" % "0.5.0" + "com.lihaoyi" %%% "utest" % "0.7.2", + "org.scala-js" %%% "scalajs-dom" % "0.9.8", + "org.webjars" % "rot.js" % "0.5.0", + "org.scala-lang.modules" %%% "scala-collection-contrib" % "0.2.1" ) npmDependencies in Compile += "rot-js" -> "0.6.2" @@ -33,8 +34,8 @@ site := { import java.nio.file.{Files, StandardCopyOption} (webpack in fullOptJS in Compile).value val targetDirectory = target.value / sitePath.value - val sourceJS = target.value / "scala-2.11" / "scalajs-bundler" / "main" / "keter-opt-bundle.js" - val sourceMap = target.value / "scala-2.11" / "scalajs-bundler" / "main" / "keter-opt-bundle.js.map" + val sourceJS = target.value / "scala-2.13" / "scalajs-bundler" / "main" / "keter-opt-bundle.js" + val sourceMap = target.value / "scala-2.13" / "scalajs-bundler" / "main" / "keter-opt-bundle.js.map" val targetJS = targetDirectory / "keter.js" val targetMap = targetDirectory / sourceMap.name targetJS.mkdirs() diff --git a/project/plugins.sbt b/project/plugins.sbt index 2b2ef90..41de966 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,3 @@ -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.25") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.31") -addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.14.0") +addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler-sjs06" % "0.16.0") diff --git a/src/main/scala/ru/org/codingteam/keter/game/IEngine.scala b/src/main/scala/ru/org/codingteam/keter/game/IEngine.scala index aa08519..da34509 100644 --- a/src/main/scala/ru/org/codingteam/keter/game/IEngine.scala +++ b/src/main/scala/ru/org/codingteam/keter/game/IEngine.scala @@ -2,5 +2,5 @@ package ru.org.codingteam.keter.game trait IEngine { - def addMessage(msg: String) + def addMessage(msg: String): Unit } diff --git a/src/main/scala/ru/org/codingteam/keter/game/Location.scala b/src/main/scala/ru/org/codingteam/keter/game/Location.scala index 881bc2e..4871068 100644 --- a/src/main/scala/ru/org/codingteam/keter/game/Location.scala +++ b/src/main/scala/ru/org/codingteam/keter/game/Location.scala @@ -95,7 +95,7 @@ object Location extends Logging { submap = submap1, coords = skel1.coordsOf('@').coords, subspaceMatrix = SubspaceMatrix.identity), - items = Set(Knife("Knife")) + items = Set(Knife("Knife"), Knife("Knife"), Knife("Knife")) ) val scp = human( diff --git a/src/main/scala/ru/org/codingteam/keter/game/objects/EventQueue.scala b/src/main/scala/ru/org/codingteam/keter/game/objects/EventQueue.scala index 838e83b..2753491 100644 --- a/src/main/scala/ru/org/codingteam/keter/game/objects/EventQueue.scala +++ b/src/main/scala/ru/org/codingteam/keter/game/objects/EventQueue.scala @@ -6,7 +6,7 @@ import scala.collection.SortedMap sealed class EventQueue(val timestamp: Double, events: SortedMap[Double, Seq[ScheduledEvent]]) { def addEvent(at: Double, event: ScheduledEvent): EventQueue = { - new EventQueue(timestamp, events + (at -> (events.getOrElse(at, IndexedSeq()) :+ event))) + new EventQueue(timestamp, events ++ Map(at -> (events.getOrElse(at, IndexedSeq()) :+ event))) } def nextEventTime: Option[Double] = events.headOption map (_._1) @@ -19,7 +19,7 @@ sealed class EventQueue(val timestamp: Double, events: SortedMap[Double, Seq[Sch require(events.nonEmpty, "Event queue is empty!") events.head match { case (at, Seq(action)) => new EventQueue(timestamp, events.tail) - case (at, actions) => new EventQueue(timestamp, events.tail + (at -> actions.tail)) + case (at, actions) => new EventQueue(timestamp, events.tail ++ Map(at -> actions.tail)) } } @@ -29,5 +29,7 @@ sealed class EventQueue(val timestamp: Double, events: SortedMap[Double, Seq[Sch } object EventQueue { + import Ordering.Double.IeeeOrdering + val empty = new EventQueue(0, SortedMap()) } diff --git a/src/main/scala/ru/org/codingteam/keter/game/objects/Inventory.scala b/src/main/scala/ru/org/codingteam/keter/game/objects/Inventory.scala index efaf507..3270ee1 100644 --- a/src/main/scala/ru/org/codingteam/keter/game/objects/Inventory.scala +++ b/src/main/scala/ru/org/codingteam/keter/game/objects/Inventory.scala @@ -14,5 +14,5 @@ case class Inventory(body: Set[Bodypart], equipment: Set[EquipmentItem], backpac /** * @return list of all inventory items either equipped or not. */ - def allEquipment = equipment ++ backpack + def allEquipment: Set[EquipmentItem] = equipment ++ backpack } diff --git a/src/main/scala/ru/org/codingteam/keter/game/objects/equipment/items/Weapon.scala b/src/main/scala/ru/org/codingteam/keter/game/objects/equipment/items/Weapon.scala index aabaae1..f164017 100644 --- a/src/main/scala/ru/org/codingteam/keter/game/objects/equipment/items/Weapon.scala +++ b/src/main/scala/ru/org/codingteam/keter/game/objects/equipment/items/Weapon.scala @@ -11,8 +11,7 @@ abstract class Weapon extends EquipmentItem { def damage: Int } -case class Knife(name: String, - damage: Int = 50) extends Weapon { +class Knife(private val _name: String, private val _damage: Int) extends Weapon { val cooldown = 200 val missCooldown = 100 @@ -37,4 +36,14 @@ case class Knife(name: String, override val requires: Set[Capability] = Set(ManipulatorCapability) + override def damage: Int = _damage + + /** + * Name of equipment item. + */ + override def name: String = _name +} + +object Knife { + def apply(name: String, damage: Int = 50) = new Knife(name, damage) } diff --git a/src/main/scala/ru/org/codingteam/keter/map/package.scala b/src/main/scala/ru/org/codingteam/keter/map/package.scala index be83abc..4324098 100644 --- a/src/main/scala/ru/org/codingteam/keter/map/package.scala +++ b/src/main/scala/ru/org/codingteam/keter/map/package.scala @@ -137,6 +137,8 @@ package object map { } def nextEvent: Option[(Double, ScheduledEvent, UniverseSnapshot)] = { + import Ordering.Double.IeeeOrdering + val delays = (globalEvents.nextEventDelay map ((_, this))).toIndexedSeq ++ actors.values.flatMap(a => a.nextEventDelay.map((_, a))) log.debug(s"Timestamp: ${globalEvents.timestamp}; First event count: ${delays.size}") @@ -148,7 +150,7 @@ package object map { val (nextTimestamp, nextAction) = globalEvents.nextEvent.get val nextUniverse = copy( globalEvents = globalEvents.dropNextEvent().addTime(delay), - actors = actors.mapValues(_.addTime(delay))) + actors = actors.view.mapValues(_.addTime(delay)).toMap) log.debug(s"Event from universe, delay=$delay, next timestamp=$nextTimestamp") (nextTimestamp, nextAction, nextUniverse) case (delay, actor: ActorLike) => @@ -156,7 +158,7 @@ package object map { val nextAction = actor.eventQueue.nextEvent.get._2 val nextUniverse = copy( globalEvents = globalEvents.addTime(delay), - actors = actors.mapValues(_.addTime(delay))).updatedActor(actor.id)(_.dropNextEvent()) + actors = actors.view.mapValues(_.addTime(delay)).toMap).updatedActor(actor.id)(_.dropNextEvent()) log.debug(s"Event from actor, delay=$delay, next timestamp=$nextTimestamp") (nextTimestamp, nextAction, nextUniverse) }) diff --git a/src/main/scala/ru/org/codingteam/keter/scenes/inventory/InventoryViewModel.scala b/src/main/scala/ru/org/codingteam/keter/scenes/inventory/InventoryViewModel.scala index 826f5c3..aa3ea30 100644 --- a/src/main/scala/ru/org/codingteam/keter/scenes/inventory/InventoryViewModel.scala +++ b/src/main/scala/ru/org/codingteam/keter/scenes/inventory/InventoryViewModel.scala @@ -2,10 +2,11 @@ package ru.org.codingteam.keter.scenes.inventory import ru.org.codingteam.keter.game.objects.Inventory import ru.org.codingteam.keter.game.objects.equipment.EquipmentCategory._ -import ru.org.codingteam.keter.game.objects.equipment.{EquipmentCategory, EquipmentItem} +import ru.org.codingteam.keter.game.objects.equipment.{Capability, EquipmentCategory, EquipmentItem} import ru.org.codingteam.keter.ui.viewmodels.{ItemsViewModel, StaticTextViewModel} import ru.org.codingteam.keter.util.VectorMap +import scala.collection.mutable import scala.concurrent.Promise class InventoryViewModel(var inventory: Inventory) { @@ -33,20 +34,31 @@ class InventoryViewModel(var inventory: Inventory) { val currentFolderItems = new ItemsViewModel[EquipmentItem](toVectorMap(inventory.allEquipment.toSeq)) { + def canEquip(item: EquipmentItem): Boolean = { + val inventoryProvidedCapabilities = inventory.body.foldLeft(mutable.MultiSet[Capability]())(_ ++= _.provides) + val inventoryRequiredCapabilities = inventory.equipment.foldLeft(mutable.MultiSet[Capability]())(_ ++= _.requires) + + inventoryProvidedCapabilities --= inventoryRequiredCapabilities + (item.requires & inventoryProvidedCapabilities.toSet) == item.requires + } + def equipped(item: EquipmentItem): Boolean = { inventory.equipment.contains(item) } - def toggleSelected() = { + def toggleSelected(): Unit = { selectedItem foreach { item => var backpack = inventory.backpack var equipment = inventory.equipment + if (equipped(item)) { equipment -= item backpack += item - } else { + } else if (canEquip(item)) { backpack -= item equipment += item + } else { + return } inventory = inventory.copy(backpack = backpack, equipment = equipment) @@ -58,6 +70,6 @@ class InventoryViewModel(var inventory: Inventory) { private def toVectorMap(items: Seq[EquipmentItem]): VectorMap[EquipmentItem, String] = { val itemsWithNames = items.map(equipment => (equipment, equipment.name)) - VectorMap(itemsWithNames.toSeq: _*) + VectorMap(itemsWithNames: _*) } } diff --git a/src/test/scala/ru/org/codingteam/keter/game/actions/ChangeInventoryActionTest.scala b/src/test/scala/ru/org/codingteam/keter/game/actions/ChangeInventoryActionTest.scala index 752a694..c4be00f 100644 --- a/src/test/scala/ru/org/codingteam/keter/game/actions/ChangeInventoryActionTest.scala +++ b/src/test/scala/ru/org/codingteam/keter/game/actions/ChangeInventoryActionTest.scala @@ -10,8 +10,8 @@ object ChangeInventoryActionTest extends TestSuite { val universe = Location.createLocation() val action = InventoryChangeAction(Inventory(Set(Head("h", 10)), Set(Knife("k")), Set())) - val tests = TestSuite { - 'shouldChangeInventory { + val tests = Tests { + test("shouldChangeInventory") { val newState = action.perform(universe.playerId.get, universe) val player = newState.player.get diff --git a/src/test/scala/ru/org/codingteam/keter/game/actions/WaitActionTest.scala b/src/test/scala/ru/org/codingteam/keter/game/actions/WaitActionTest.scala index 2d85a62..cb8c778 100644 --- a/src/test/scala/ru/org/codingteam/keter/game/actions/WaitActionTest.scala +++ b/src/test/scala/ru/org/codingteam/keter/game/actions/WaitActionTest.scala @@ -9,8 +9,8 @@ object WaitActionTest extends TestSuite { val universe = UniverseSnapshot(Map(), Some(playerId), EventQueue.empty) val action = WaitAction(50.0) - val tests = TestSuite { - 'shouldWait { + val tests = Tests { + test("shouldWait") { val newState = action.perform(playerId, universe) assert(newState == universe) } diff --git a/src/test/scala/ru/org/codingteam/keter/game/objects/InventoryTest.scala b/src/test/scala/ru/org/codingteam/keter/game/objects/InventoryTest.scala new file mode 100644 index 0000000..f1e66a9 --- /dev/null +++ b/src/test/scala/ru/org/codingteam/keter/game/objects/InventoryTest.scala @@ -0,0 +1,16 @@ +package ru.org.codingteam.keter.game.objects + +import ru.org.codingteam.keter.game.objects.equipment.items.Knife +import utest._ + +import scala.collection.MultiSet + +object InventoryTest extends TestSuite { + val tests = Tests { + test("shouldSetTwoKnifes") { + val i = Inventory(Set(), Set(Knife("Knife")), Set(Knife("Knife"))) + println(i.allEquipment) + assert(i.allEquipment.size == 2) + } + } +}