├── .gitignore
├── .scalafmt.conf
├── README.md
├── build.sbt
├── collisions
└── shared
│ └── src
│ ├── main
│ └── scala
│ │ └── cwinter
│ │ └── codecraft
│ │ └── collisions
│ │ ├── Positionable.scala
│ │ ├── SquareGrid.scala
│ │ └── VisionTracker.scala
│ └── test
│ └── scala
│ └── cwinter
│ └── codecraft
│ └── collisions
│ ├── SquareGridTest.scala
│ └── VisionTrackerTest.scala
├── core
├── js
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── cwinter
│ │ └── codecraft
│ │ └── core
│ │ ├── PerformanceMonitorFactory.scala
│ │ ├── api
│ │ ├── JSDroneController.scala
│ │ └── TheGameMaster.scala
│ │ ├── game
│ │ └── CrossPlatformAwait.scala
│ │ ├── multiplayer
│ │ ├── CrossPlatformWebsocket.scala
│ │ └── JSWebsocketClient.scala
│ │ └── replay
│ │ └── ReplayFactory.scala
├── jvm
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── cwinter
│ │ │ └── codecraft
│ │ │ └── core
│ │ │ ├── IntraRuntimeMultiplayerTest.scala
│ │ │ ├── LoadTest.scala
│ │ │ ├── MultiplayerTest.scala
│ │ │ ├── PerformanceMonitorFactory.scala
│ │ │ ├── StartMulticlientServer.scala
│ │ │ ├── WebsocketMulticlientTest.scala
│ │ │ ├── WebsocketMultiplayerTest.scala
│ │ │ ├── api
│ │ │ └── TheGameMaster.scala
│ │ │ ├── game
│ │ │ └── CrossPlatformAwait.scala
│ │ │ ├── multiplayer
│ │ │ ├── CrossPlatformWebsocket.scala
│ │ │ ├── JavaXWebsocketClient.scala
│ │ │ ├── Server.scala
│ │ │ ├── WebsocketActor.scala
│ │ │ └── WebsocketClientConnection.scala
│ │ │ └── replay
│ │ │ ├── FileReplayRecorder.scala
│ │ │ └── ReplayFactory.scala
│ │ └── test
│ │ └── scala
│ │ └── cwinter
│ │ └── codecraft
│ │ └── core
│ │ ├── ConstantVelocityDynamicsTest.scala
│ │ ├── TestUtils.scala
│ │ ├── game
│ │ ├── DroneWorldSimulatorTest.scala
│ │ └── TightCollisionTest.scala
│ │ ├── objects
│ │ └── drone
│ │ │ ├── DroneFactory.scala
│ │ │ ├── DroneModuleTestHelper.scala
│ │ │ ├── MissileBatteryModuleTest.scala
│ │ │ ├── ShieldGeneratorModuleTest.scala
│ │ │ └── StorageModuleTest.scala
│ │ └── replay
│ │ ├── MultiplayerSemanticsTest.scala
│ │ └── ReplayTest.scala
└── shared
│ └── src
│ └── main
│ └── scala
│ └── cwinter
│ └── codecraft
│ └── core
│ ├── PerformanceMonitor.scala
│ ├── ai
│ ├── basic
│ │ └── Main.scala
│ ├── basicplus
│ │ ├── BasicPlusController.scala
│ │ ├── Destroyer.scala
│ │ ├── Hunter.scala
│ │ ├── Mothership.scala
│ │ ├── ScoutingDroneController.scala
│ │ └── SearchToken.scala
│ ├── cheese
│ │ ├── Destroyer.scala
│ │ └── Mothership.scala
│ ├── destroyer
│ │ ├── Destroyer.scala
│ │ ├── DestroyerContext.scala
│ │ ├── DestroyerController.scala
│ │ ├── Harvester.scala
│ │ ├── Mothership.scala
│ │ └── Scout.scala
│ ├── deterministic
│ │ └── Main.scala
│ ├── replicator
│ │ ├── Harvester.scala
│ │ ├── MothershipCoordinator.scala
│ │ ├── Replicator.scala
│ │ ├── ReplicatorContext.scala
│ │ ├── ReplicatorController.scala
│ │ ├── Soldier.scala
│ │ ├── TargetAcquisition.scala
│ │ ├── Util.scala
│ │ └── combat
│ │ │ ├── AssaultCapitalShip.scala
│ │ │ ├── Assist.scala
│ │ │ ├── EliminateEnemy.scala
│ │ │ ├── Guard.scala
│ │ │ ├── KeepEyeOnEnemy.scala
│ │ │ ├── ReplicatorBattleCoordinator.scala
│ │ │ ├── ReplicatorCommand.scala
│ │ │ └── ScoutingMission.scala
│ └── shared
│ │ ├── AugmentedController.scala
│ │ ├── BasicHarvestCoordinator.scala
│ │ ├── BattleCoordinator.scala
│ │ ├── DroneCounter.scala
│ │ ├── HarvestCoordinatorWithZones.scala
│ │ ├── Mission.scala
│ │ ├── SearchCoordinator.scala
│ │ ├── SearchToken.scala
│ │ └── SharedContext.scala
│ ├── api
│ ├── CodeCraftException.scala
│ ├── Drone.scala
│ ├── DroneController.scala
│ ├── DroneControllerBase.scala
│ ├── DroneSpec.scala
│ ├── GameConstants.scala
│ ├── GameMasterLike.scala
│ ├── JDroneController.scala
│ ├── MetaController.scala
│ ├── MineralCrystal.scala
│ ├── ObjectNotVisibleException.scala
│ └── Player.scala
│ ├── errors
│ ├── ErrorMessageObject.scala
│ └── Errors.scala
│ ├── game
│ ├── CommandRecorder.scala
│ ├── DroneWorldSimulator.scala
│ ├── GameConfig.scala
│ ├── MultiplayerConfig.scala
│ ├── Settings.scala
│ ├── SimulationContext.scala
│ ├── SpecialRules.scala
│ ├── WinCondition.scala
│ └── WorldMap.scala
│ ├── graphics
│ ├── BasicHomingMissileModel.scala
│ ├── CollisionMarkerModel.scala
│ ├── ConstructionBeamsModel.scala
│ ├── DroneConstructorModel.scala
│ ├── DroneEnginesModel.scala
│ ├── DroneMissileBatteryModel.scala
│ ├── DroneModel.scala
│ ├── DroneShieldGeneratorModel.scala
│ ├── DroneStorageModel.scala
│ ├── DroneThrusterTrailsModelFactory.scala
│ ├── EnergyGlobeModel.scala
│ ├── EnergyGlobeModelFactory.scala
│ ├── HarvestingBeamsModel.scala
│ ├── HomingMissileModel.scala
│ ├── LightFlashModel.scala
│ └── MineralCrystalModel.scala
│ ├── multiplayer
│ ├── LocalClientConnection.scala
│ ├── RemoteClient.scala
│ ├── RemoteServer.scala
│ ├── ServerConnection.scala
│ ├── ServerStatus.scala
│ ├── WebsocketClient.scala
│ └── WebsocketServerConnection.scala
│ ├── objects
│ ├── ConstantVelocityDynamics.scala
│ ├── EnergyGlobeObject.scala
│ ├── HomingMissile.scala
│ ├── IDGenerator.scala
│ ├── LightFlash.scala
│ ├── MineralCrystalImpl.scala
│ ├── MissileDynamics.scala
│ ├── WorldObject.scala
│ └── drone
│ │ ├── ComputedDroneDynamics.scala
│ │ ├── ConstructorModule.scala
│ │ ├── DroneContext.scala
│ │ ├── DroneDebugLog.scala
│ │ ├── DroneDynamicsBasics.scala
│ │ ├── DroneEvent.scala
│ │ ├── DroneEventQueue.scala
│ │ ├── DroneGraphicsHandler.scala
│ │ ├── DroneHull.scala
│ │ ├── DroneImpl.scala
│ │ ├── DroneMessageDisplay.scala
│ │ ├── DroneModule.scala
│ │ ├── DroneModules.scala
│ │ ├── DroneMovementDetector.scala
│ │ ├── DroneVisionTracker.scala
│ │ ├── EnemyDrone.scala
│ │ ├── EnginesModule.scala
│ │ ├── MissileBatteryModule.scala
│ │ ├── RemoteDroneDynamics.scala
│ │ ├── ShieldGeneratorModule.scala
│ │ ├── SpeculatingDroneDynamics.scala
│ │ └── StorageModule.scala
│ └── replay
│ ├── ConsoleReplayRecorder.scala
│ ├── DummyDroneController.scala
│ ├── NullReplayRecorder.scala
│ ├── Replay.scala
│ ├── ReplayRecorder.scala
│ ├── Replayer.scala
│ └── StringReplayRecorder.scala
├── docs
└── root-doc.txt
├── graphics
├── js
│ └── src
│ │ └── main
│ │ ├── resources
│ │ ├── rgb1_brighten_fs.glsl
│ │ ├── rgb1_fs.glsl
│ │ ├── rgba_fs.glsl
│ │ ├── rgba_gaussian_fs.glsl
│ │ ├── rgba_gaussian_pint_fs.glsl
│ │ ├── rgba_pint_fs.glsl
│ │ ├── xyz_rgb_vs.glsl
│ │ └── xyz_rgba_vs.glsl
│ │ └── scala
│ │ └── cwinter
│ │ └── codecraft
│ │ └── graphics
│ │ ├── engine
│ │ ├── GraphicsEngine.scala
│ │ ├── JSRenderStack.scala
│ │ ├── Renderer.scala
│ │ └── WebGLRenderer.scala
│ │ ├── materials
│ │ ├── GaussianGlow.scala
│ │ ├── GaussianGlowPIntensity.scala
│ │ ├── JSMaterial.scala
│ │ ├── MaterialBrightenedXYZRGB.scala
│ │ ├── MaterialXYZRGB.scala
│ │ ├── TranslucentAdditive.scala
│ │ └── TranslucentAdditivePIntensity.scala
│ │ └── model
│ │ └── JSVBO.scala
├── jvm
│ └── src
│ │ └── main
│ │ ├── resources
│ │ ├── 110_rgb1_fs.glsl
│ │ ├── 110_rgba_fs.glsl
│ │ ├── 110_rgba_gaussian_pint_fs.glsl
│ │ ├── 110_rgba_pint_fs.glsl
│ │ ├── 110_xyz_rgb_vs.glsl
│ │ ├── 110_xyz_rgba_vs.glsl
│ │ ├── basic_fs.glsl
│ │ ├── basic_vs.glsl
│ │ ├── convolution_fs.glsl
│ │ ├── rgb0_fs.glsl
│ │ ├── rgb1_fs.glsl
│ │ ├── rgba_fs.glsl
│ │ ├── rgba_gaussian_fs.glsl
│ │ ├── rgba_gaussian_pint_fs.glsl
│ │ ├── rgba_pint_fs.glsl
│ │ ├── texture_xy_fs.glsl
│ │ ├── texture_xy_vs.glsl
│ │ ├── xy_rgb_vs.glsl
│ │ ├── xyz_rgb_vs.glsl
│ │ └── xyz_rgba_vs.glsl
│ │ └── scala
│ │ └── cwinter
│ │ └── codecraft
│ │ └── graphics
│ │ ├── application
│ │ └── DrawingCanvas.scala
│ │ ├── engine
│ │ ├── FramebufferObject.scala
│ │ ├── GraphicsEngine.scala
│ │ ├── JVMAsyncRunner.scala
│ │ ├── JVMGL2RenderStack.scala
│ │ ├── JVMRenderStack.scala
│ │ └── RenderFrame.scala
│ │ ├── materials
│ │ ├── BloomShader.scala
│ │ ├── GaussianGlow.scala
│ │ ├── GaussianGlowPIntensity.scala
│ │ ├── GaussianGlowPIntensity110.scala
│ │ ├── JVMMaterial.scala
│ │ ├── MaterialXYZRGB.scala
│ │ ├── MaterialXYZRGB110.scala
│ │ ├── RenderToScreen.scala
│ │ ├── SimpleMaterial.scala
│ │ ├── TranslucentAdditive.scala
│ │ ├── TranslucentAdditive110.scala
│ │ ├── TranslucentAdditivePIntensity.scala
│ │ ├── TranslucentAdditivePIntensity110.scala
│ │ └── TranslucentProportional.scala
│ │ └── model
│ │ └── JVMVBO.scala
└── shared
│ └── src
│ └── main
│ └── scala
│ └── cwinter
│ └── codecraft
│ └── graphics
│ ├── engine
│ ├── Camera2D.scala
│ ├── Debug.scala
│ ├── GraphicsContext.scala
│ ├── KeyEventHandler.scala
│ ├── ModelDescriptor.scala
│ ├── PositionDescriptor.scala
│ ├── RenderStack.scala
│ ├── Simulator.scala
│ ├── TextModel.scala
│ └── WorldObjectDescriptor.scala
│ ├── materials
│ ├── Intensity.scala
│ └── Material.scala
│ ├── model
│ ├── ClosedModel.scala
│ ├── CompositeModel.scala
│ ├── CompositeModelBuilder.scala
│ ├── DecoratorModel.scala
│ ├── DynamicModel.scala
│ ├── DynamicVertexCountModel.scala
│ ├── EmptyModel.scala
│ ├── EmptyModelBuilder.scala
│ ├── HideableModel.scala
│ ├── IdentityModelviewModel.scala
│ ├── ImmediateModeModel.scala
│ ├── Model.scala
│ ├── ModelBuilder.scala
│ ├── ParameterPreservingDecoratorModel.scala
│ ├── PrimitiveModelBuilder.scala
│ ├── ProjectedParamsModel.scala
│ ├── ProjectedParamsModelBuilder.scala
│ ├── ScalableModel.scala
│ ├── SimpleModelBuilder.scala
│ ├── StaticCompositeModel.scala
│ ├── StaticModel.scala
│ ├── TheCompositeModelBuilderCache.scala
│ ├── TheModelCache.scala
│ ├── TranslatedModel.scala
│ ├── TypedCache.scala
│ ├── VBO.scala
│ ├── VertexCollectionModelBuilder.scala
│ └── package.scala
│ ├── models
│ ├── CircleModelBuilder.scala
│ ├── CircleOutlineModelBuilder.scala
│ ├── RectangleModelBuilder.scala
│ └── TestModel.scala
│ └── primitives
│ ├── LinePrimitive.scala
│ ├── PartialPolygon.scala
│ ├── PartialPolygonRing.scala
│ ├── Polygon.scala
│ ├── PolygonRing.scala
│ ├── PolygonWave.scala
│ ├── QuadStrip.scala
│ ├── RectanglePrimitive.scala
│ ├── RichQuadStrip.scala
│ └── SquarePrimitive.scala
├── physics
└── shared
│ └── src
│ ├── main
│ └── scala
│ │ └── cwinter
│ │ └── codecraft
│ │ └── physics
│ │ ├── Dynamics.scala
│ │ └── PhysicsEngine.scala
│ └── test
│ └── scala
│ └── cwinter
│ └── codecraft
│ └── physics
│ └── ConstantVelocity$Test.scala
├── project
├── Commons.scala
├── Dependencies.scala
├── assembly.sbt
├── build.properties
└── plugins.sbt
├── scalajs-test
└── src
│ └── main
│ ├── resources
│ └── index-dev.html
│ └── scala
│ └── cwinter
│ └── codecraft
│ └── scalajs
│ └── Main.scala
├── testai
└── src
│ └── main
│ └── scala
│ └── cwinter
│ └── codecraft
│ └── testai
│ ├── RunBenchmark.scala
│ ├── RunLastReplay.scala
│ └── RunTestAI.scala
└── util
├── jvm
└── src
│ └── main
│ └── scala
│ └── scala
│ └── scalajs
│ └── js
│ └── annotation
│ ├── JSExport.scala
│ └── JSExportAll.scala
└── shared
└── src
├── main
└── scala
│ └── cwinter
│ └── codecraft
│ └── util
│ ├── AggregateStatistics.scala
│ ├── CompileTimeLoader.scala
│ ├── PrecomputedHashcode.scala
│ ├── Stopwatch.scala
│ ├── maths
│ ├── Float0To1.scala
│ ├── Functions.scala
│ ├── Geometry.scala
│ ├── RNG.scala
│ ├── Rectangle.scala
│ ├── Solve.scala
│ ├── Vector2.scala
│ ├── Vertex.scala
│ └── matrices
│ │ ├── DilationMatrix4x4.scala
│ │ ├── DilationXYMatrix4x4.scala
│ │ ├── IdentityMatrix4x4.scala
│ │ ├── Matrix2x2.scala
│ │ ├── Matrix4x4.scala
│ │ ├── OrthographicProjectionMatrix4x4.scala
│ │ ├── RotationZMatrix4x4.scala
│ │ ├── RotationZTranslationXYMatrix4x4.scala
│ │ ├── RotationZTranslationXYTransposedMatrix4x4.scala
│ │ ├── TranslationMatrix4x4.scala
│ │ └── TranslationXYMatrix4x4.scala
│ └── modules
│ └── ModulePosition.scala
└── test
└── scala
└── cwinter
└── codecraft
└── util
└── maths
├── Matrix4x4Test.scala
└── Solve$Test.scala
/.gitignore:
--------------------------------------------------------------------------------
1 | # Scala
2 | target
3 | *.class
4 |
5 | # IntelliJ
6 | *.impl
7 | *.ipr
8 | *.iws
9 | .idea
10 | out
11 |
12 |
13 | # Vim
14 | tags
15 | .*.swp
16 | .*.swo
17 |
18 | # OpenGL
19 | *.log
20 |
21 | # nohup
22 | nohup.out
23 |
--------------------------------------------------------------------------------
/.scalafmt.conf:
--------------------------------------------------------------------------------
1 | maxColumn = 110
2 | continuationIndent.defnSite = 2
3 |
--------------------------------------------------------------------------------
/collisions/shared/src/main/scala/cwinter/codecraft/collisions/Positionable.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.collisions
2 |
3 | import cwinter.codecraft.util.maths.Vector2
4 |
5 | private[codecraft] trait Positionable[-T] {
6 | def position(t: T): Vector2
7 | }
8 |
9 | private[codecraft] object Positionable {
10 | final implicit class PositionableOps[T](t: T)(implicit ev: Positionable[T]) {
11 | @inline def position: Vector2 = ev.position(t)
12 | }
13 |
14 | implicit object Vector2IsPositionable extends Positionable[Vector2] {
15 | override def position(t: Vector2): Vector2 = t
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/collisions/shared/src/test/scala/cwinter/codecraft/collisions/SquareGridTest.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.collisions
2 |
3 | import cwinter.codecraft.util.maths.Vector2
4 | import org.scalatest.FlatSpec
5 |
6 |
7 | class SquareGridTest extends FlatSpec {
8 | "A square grid" should "assign objects to the correct cell" in {
9 |
10 | val squareGrid = new SquareGrid[Vector2](-2000, 2000, -1000, 1000, 1)
11 |
12 | def checkCell(point: Vector2): Unit = {
13 | val cell = squareGrid.computeCell(point)
14 | val bounds = squareGrid.cellBounds(cell._1, cell._2)
15 | assert(bounds.contains(point))
16 | }
17 |
18 | checkCell(Vector2(0, 0))
19 | checkCell(Vector2(0, -123))
20 | checkCell(Vector2(-2000, 1000))
21 | checkCell(Vector2(0.9999999, -1))
22 | checkCell(Vector2(1.0000001, 999.999999))
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/core/js/src/main/scala/cwinter/codecraft/core/PerformanceMonitorFactory.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core
2 |
3 | object PerformanceMonitorFactory {
4 | def performanceMonitor: PerformanceMonitor = new MockPerformanceMonitor()
5 | }
6 |
7 |
8 |
--------------------------------------------------------------------------------
/core/js/src/main/scala/cwinter/codecraft/core/game/CrossPlatformAwait.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.game
2 |
3 | import scala.concurrent.Awaitable
4 | import scala.concurrent.duration.Duration
5 |
6 |
7 | object CrossPlatformAwait {
8 | def result[T](awaitable: Awaitable[T], atMost: Duration): T =
9 | throw new Exception("Trying to Await in JavaScript!")
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/core/js/src/main/scala/cwinter/codecraft/core/multiplayer/CrossPlatformWebsocket.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.multiplayer
2 |
3 | object CrossPlatformWebsocket {
4 | def create(connectionString: String): WebsocketClient =
5 | new JSWebsocketClient(connectionString)
6 | }
7 |
--------------------------------------------------------------------------------
/core/js/src/main/scala/cwinter/codecraft/core/multiplayer/JSWebsocketClient.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.multiplayer
2 |
3 | import java.nio.ByteBuffer
4 |
5 | import org.scalajs.dom.raw._
6 |
7 | import scala.scalajs.js.typedarray.TypedArrayBufferOps._
8 | import scala.scalajs.js.typedarray.{ArrayBuffer, TypedArrayBuffer}
9 |
10 | private[core] class JSWebsocketClient(connectionString: String) extends WebsocketClient {
11 | var ws = Option.empty[WebSocket]
12 |
13 | def connect(): Unit = {
14 | val ws = new WebSocket(connectionString)
15 | this.ws = Some(ws)
16 |
17 | ws.onmessage = (event: MessageEvent) => {
18 | event.data match {
19 | case blob: Blob =>
20 | val reader = new FileReader
21 | reader.addEventListener("loadend", (x: Any) => {
22 | val buffer = TypedArrayBuffer.wrap(reader.result.asInstanceOf[ArrayBuffer])
23 | runOnMessageCallbacks(buffer)
24 | })
25 | reader.readAsArrayBuffer(blob)
26 | case _ => throw new Exception("Received message with unexpected encoding.")
27 | }
28 | }
29 |
30 | ws.onopen = (event: Event) => {
31 | runOnOpenCallbacks()
32 | }
33 |
34 | ws.onclose = (event: Event) => {
35 | runOnCloseCallbacks()
36 | }
37 | }
38 |
39 | def sendMessage(message: ByteBuffer): Unit = ws match {
40 | case Some(s) => s.send(message.arrayBuffer())
41 | case None => throw new IllegalStateException("Need to connect() before sending messages.")
42 | }
43 |
44 | def isConnecting: Boolean = ws.forall(_.readyState == 0)
45 | }
46 |
--------------------------------------------------------------------------------
/core/js/src/main/scala/cwinter/codecraft/core/replay/ReplayFactory.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.replay
2 |
3 | private[core] object ReplayFactory {
4 | def replayRecorder: ReplayRecorder = new StringReplayRecorder
5 | }
6 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/cwinter/codecraft/core/LoadTest.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core
2 |
3 | import java.util.concurrent.Executors
4 |
5 | import cwinter.codecraft.core.api.{DroneControllerBase, TheGameMaster}
6 |
7 | import scala.concurrent.ExecutionContext
8 | import scala.util.{Failure, Success}
9 |
10 | private[core] object LoadTest {
11 | implicit val executionContext = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(1000))
12 | val MaxGames = 10000
13 | val ServerURL = "localhost"
14 | val IntervalMS = 500
15 |
16 | def main(args: Array[String]): Unit = {
17 | for (i <- 0 to MaxGames) {
18 | spawnGame(i)
19 | Thread.sleep(IntervalMS)
20 | }
21 | }
22 |
23 | def spawnGame(i: Int): Unit = {
24 | println(s"Spawning Game $i")
25 | spawnConnection(TheGameMaster.replicatorAI(), s"$i-rep")
26 | spawnConnection(TheGameMaster.destroyerAI(), s"$i-des")
27 | }
28 |
29 | def spawnConnection(ai: => DroneControllerBase, id: String): Unit =
30 | TheGameMaster.prepareMultiplayerGame(ServerURL, ai).onComplete {
31 | case Success(game) =>
32 | println(s"Connection Success for $id")
33 | game.runInContext()
34 | case Failure(x) =>
35 | println(s"Connection Failure($x) for $id")
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/cwinter/codecraft/core/MultiplayerTest.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core
2 |
3 | import cwinter.codecraft.core.api.TheGameMaster
4 |
5 | import scala.concurrent.Await
6 | import scala.concurrent.duration._
7 |
8 |
9 | private[core] object MultiplayerTest {
10 | def main(args: Array[String]): Unit = {
11 | val client = Await.result(
12 | TheGameMaster.prepareMultiplayerGame("localhost", TheGameMaster.replicatorAI()), 10.seconds)
13 | TheGameMaster.run(client)
14 | }
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/cwinter/codecraft/core/PerformanceMonitorFactory.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core
2 |
3 | import etm.core.configuration.{BasicEtmConfigurator, EtmManager}
4 | import etm.core.monitor.EtmPoint
5 | import etm.core.renderer.SimpleTextRenderer
6 |
7 |
8 | private[codecraft] object PerformanceMonitorFactory {
9 | @volatile var assigned = false
10 |
11 | def performanceMonitor: PerformanceMonitor = new SimplePerformanceMonitor
12 | }
13 |
14 |
15 | private[codecraft] class JETMPerformanceMonitor extends PerformanceMonitor {
16 | private val debug = false
17 | private var activePoints = Map.empty[Symbol, EtmPoint]
18 |
19 | BasicEtmConfigurator.configure(true)
20 | private val monitor = EtmManager.getEtmMonitor
21 | monitor.start()
22 |
23 |
24 | override def measure[T](name: Symbol)(code: => T): T = {
25 | if (debug) println(s">$name")
26 | val point = synchronized { monitor.createPoint(name.toString) }
27 | val result = try {
28 | code
29 | } finally {
30 | synchronized { point.collect() }
31 | }
32 | if (debug) println(s"<$name")
33 | result
34 | }
35 |
36 | override def beginMeasurement(name: Symbol): Unit = synchronized {
37 | if (debug) println(s">$name")
38 | val point = monitor.createPoint(name.toString)
39 | activePoints += name -> point
40 | }
41 |
42 | override def endMeasurement(name: Symbol): Unit = synchronized {
43 | activePoints.get(name) match {
44 | case Some(point) =>
45 | point.collect()
46 | activePoints -= name
47 | if (debug) println(s"<$name")
48 | case None =>
49 | throw new Exception(s"Tried to end measurement of $name, but there is no active point for $name.")
50 | }
51 | }
52 |
53 |
54 | override def compileReport: String = synchronized {
55 | monitor.render(new SimpleTextRenderer)
56 | ""
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/cwinter/codecraft/core/StartMulticlientServer.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core
2 |
3 | private[core] object StartMulticlientServer {
4 | def main(args: Array[String]): Unit = {
5 | multiplayer.Server.spawnServerInstance2()
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/cwinter/codecraft/core/WebsocketMulticlientTest.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core
2 |
3 | import cwinter.codecraft.core.api.TheGameMaster
4 | import cwinter.codecraft.core.objects.drone.MultiplayerMessage
5 |
6 | import scala.async.Async.{async, await}
7 | import scala.concurrent.ExecutionContext.Implicits.global
8 |
9 | private[core] object WebsocketMulticlientTest {
10 | def main(args: Array[String]): Unit = {
11 | new Thread {
12 | override def run(): Unit = {
13 | multiplayer.Server.spawnServerInstance2()
14 | }
15 | }.start()
16 |
17 | Thread.sleep(2000, 0)
18 |
19 | async {
20 | val client1 = await {
21 | TheGameMaster.prepareMultiplayerGame("localhost", TheGameMaster.level2AI())
22 | }
23 | client1.run()
24 | Thread.sleep(35000, 0)
25 | val client2 = await {
26 | TheGameMaster.prepareMultiplayerGame("localhost", TheGameMaster.level2AI())
27 | }
28 | val client3 = await {
29 | TheGameMaster.prepareMultiplayerGame("localhost", TheGameMaster.level2AI())
30 | }
31 | client2.run()
32 | client3.run()
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/cwinter/codecraft/core/WebsocketMultiplayerTest.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core
2 |
3 | import cwinter.codecraft.core.api.TheGameMaster
4 |
5 | import scala.async.Async.{async, await}
6 | import scala.concurrent.ExecutionContext.Implicits.global
7 |
8 | private[core] object WebsocketMultiplayerTest {
9 | def main(args: Array[String]): Unit = {
10 | new Thread {
11 | override def run(): Unit = {
12 | multiplayer.Server.spawnServerInstance2()
13 | }
14 | }.start()
15 |
16 | Thread.sleep(2000, 0)
17 |
18 | val client1Fut = TheGameMaster.prepareMultiplayerGame("localhost", TheGameMaster.replicatorAI())
19 | val client2Fut = TheGameMaster.prepareMultiplayerGame("localhost", TheGameMaster.replicatorAI())
20 | for {
21 | client1 <- client1Fut
22 | client2 <- client2Fut
23 | } {
24 | client2.framerateTarget = 1001
25 | client2.run()
26 | TheGameMaster.run(client1)
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/cwinter/codecraft/core/api/TheGameMaster.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.api
2 |
3 | import java.io.File
4 |
5 | import cwinter.codecraft.core.game.DroneWorldSimulator
6 | import cwinter.codecraft.core.multiplayer.{JavaXWebsocketClient, WebsocketClient}
7 | import cwinter.codecraft.graphics.application.DrawingCanvas
8 |
9 | /** Main entry point to start the game. */
10 | object TheGameMaster extends GameMasterLike {
11 | override def run(simulator: DroneWorldSimulator, onComplete: () => Unit = () => {}): DroneWorldSimulator = {
12 | DrawingCanvas.run(simulator)
13 | simulator
14 | }
15 |
16 | /** Runs the replay stored in the specified file. */
17 | def runReplayFromFile(filepath: String): DroneWorldSimulator = {
18 | val simulator = createReplaySimulator(scala.io.Source.fromFile(filepath).mkString)
19 | run(simulator)
20 | }
21 |
22 | /** Runs the last recorded replay. */
23 | def runLastReplay(): DroneWorldSimulator = {
24 | val dir = new File(System.getProperty("user.home") + "/.codecraft/replays")
25 | val latest = dir.listFiles().maxBy(_.lastModified())
26 | runReplayFromFile(latest.getPath)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/cwinter/codecraft/core/game/CrossPlatformAwait.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.game
2 |
3 | import scala.concurrent.{Await, Awaitable}
4 | import scala.concurrent.duration.Duration
5 |
6 |
7 | private[codecraft] object CrossPlatformAwait {
8 | def result[T](awaitable: Awaitable[T], atMost: Duration): T =
9 | Await.result(awaitable, atMost)
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/cwinter/codecraft/core/multiplayer/CrossPlatformWebsocket.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.multiplayer
2 |
3 |
4 | object CrossPlatformWebsocket {
5 | def create(connectionString: String): WebsocketClient =
6 | new JavaXWebsocketClient(connectionString)
7 | }
8 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/cwinter/codecraft/core/replay/FileReplayRecorder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.replay
2 |
3 | import java.io.{File, FileWriter}
4 |
5 | import org.joda.time.DateTime
6 | import org.joda.time.format.DateTimeFormat
7 |
8 | import scala.annotation.tailrec
9 |
10 | private[core] class FileReplayRecorder(path: String) extends ReplayRecorder {
11 | final val format = DateTimeFormat.forPattern("YYMMdd-HHmmss")
12 | val replay = new StringBuilder
13 | val dir: Boolean = new File(path).mkdirs()
14 | val replayFile: File = createNewFile(format.print(new DateTime), ".replay")
15 | val writer = new FileWriter(replayFile)
16 |
17 |
18 | @tailrec final def createNewFile(filenamePrefix: String, filenameSuffix: String, attempt: Int = 0): File = {
19 | val infix =
20 | if (attempt == 0) ""
21 | else s" ($attempt)"
22 | val f = new File(path + "/" + filenamePrefix + infix + filenameSuffix)
23 | if (f.createNewFile()) f
24 | else createNewFile(filenamePrefix, filenameSuffix, attempt + 1)
25 | }
26 |
27 | protected override def writeLine(string: String): Unit = {
28 | writer.write(string + "\n")
29 | writer.flush()
30 | }
31 |
32 | def filename: File = replayFile.getAbsoluteFile
33 |
34 | override def replayFilepath = Some(filename.toString)
35 | }
36 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/cwinter/codecraft/core/replay/ReplayFactory.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.replay
2 |
3 | private[core] object ReplayFactory {
4 | def replayRecorder: ReplayRecorder =
5 | new FileReplayRecorder(System.getProperty("user.home") + "/.codecraft/replays")
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/cwinter/codecraft/core/ConstantVelocityDynamicsTest.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core
2 |
3 | import cwinter.codecraft.core.objects.ConstantVelocityDynamics
4 | import cwinter.codecraft.util.maths.{Rectangle, Vector2}
5 | import org.scalatest.FlatSpec
6 |
7 | class ConstantVelocityDynamicsTest extends FlatSpec {
8 | object ConstVelTestDynamics extends ConstantVelocityDynamics(1, 0, true, Vector2.Null, 0) {
9 | override def handleWallCollision(areaBounds: Rectangle): Unit = ()
10 | override def handleObjectCollision(other: ConstantVelocityDynamics): Unit = ()
11 | def setVelocity(v: Vector2): Unit = velocity = v
12 | }
13 |
14 | "ConstantVelocityDynamics" should "move with linear velocity" in {
15 | ConstVelTestDynamics.setVelocity(Vector2(1, 1))
16 | ConstVelTestDynamics.updatePosition(10)
17 | assert(ConstVelTestDynamics.pos == Vector2(10, 10))
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/cwinter/codecraft/core/game/DroneWorldSimulatorTest.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.game
2 |
3 | import cwinter.codecraft.core.api._
4 | import cwinter.codecraft.core.objects.drone.HarvestMineral
5 | import cwinter.codecraft.core.replay.{NullReplayRecorder, DummyDroneController}
6 | import cwinter.codecraft.util.maths.{Rectangle, Vector2}
7 | import org.scalatest.{FlatSpec, Matchers}
8 |
9 | class DroneWorldSimulatorTest extends FlatSpec with Matchers {
10 | val mineralSpawn = new MineralSpawn(1, Vector2(0, 0))
11 | val mockDroneSpec = new DroneSpec(storageModules = 2)
12 | val mockDrone = new DummyDroneController
13 |
14 | val map = new WorldMap(
15 | Seq(mineralSpawn), Rectangle(-2000, 2000, -2000, 2000),
16 | Seq(Spawn(mockDroneSpec, Vector2(0, 0), BluePlayer))
17 | )
18 |
19 | val config = map.createGameConfig(Seq(mockDrone), winConditions = Seq(LargestFleet(999999999)))
20 |
21 | val simulator = new DroneWorldSimulator(config, forceReplayRecorder = Some(NullReplayRecorder))
22 | val mineral = simulator.minerals.head
23 |
24 |
25 | "Game simulator" must "allow for mineral harvesting" in {
26 | mockDrone.drone ! HarvestMineral(mineral)
27 | simulator.run(GameConstants.HarvestingInterval)
28 | assert(mineral.harvested)
29 | }
30 |
31 | it must "prevent double harvesting of resources" in {
32 | mockDrone.drone ! HarvestMineral(mineral)
33 | simulator.run(GameConstants.HarvestingInterval)
34 | mockDrone.storedResources shouldBe 1
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/cwinter/codecraft/core/game/TightCollisionTest.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.game
2 |
3 | import cwinter.codecraft.core.api._
4 | import cwinter.codecraft.core.replay.{DummyDroneController, NullReplayRecorder}
5 | import cwinter.codecraft.util.maths.{Rectangle, Vector2}
6 | import org.scalatest.{FlatSpec, Matchers}
7 |
8 | import scala.concurrent.ExecutionContext.Implicits.global
9 | import scala.concurrent.duration._
10 | import scala.concurrent.{Await, Future}
11 |
12 | class TightCollisionTest extends FlatSpec with Matchers {
13 | val mockDroneSpec = new DroneSpec(storageModules = 2)
14 | val corneredDrone = new DummyDroneController
15 | val movingIntoCorner = new DummyDroneController
16 |
17 | val corner = Vector2(2000, 2000)
18 | val map = new WorldMap(
19 | Seq(),
20 | Rectangle(-2000, 2000, -2000, 2000),
21 | Seq(
22 | Spawn(mockDroneSpec, corner, BluePlayer),
23 | Spawn(mockDroneSpec, corner - mockDroneSpec.radius * Vector2(2, 2), BluePlayer)
24 | )
25 | )
26 |
27 | val config = map.createGameConfig(Seq(corneredDrone, movingIntoCorner))
28 | val simulator = new DroneWorldSimulator(config, forceReplayRecorder = Some(NullReplayRecorder))
29 |
30 | "A collision with a drone positioned on the corner" should
31 | "not send PhysicsEngine into an infinite loop" in {
32 | movingIntoCorner.moveInDirection(Vector2(1, 1))
33 | val runFor10Steps = Future {
34 | simulator.run(10)
35 | }
36 | Await.result(runFor10Steps, 5.seconds)
37 | }
38 |
39 | // TODO: fix this bug and enable test
40 | "A collision with a drone positioned on the world boundary" should
41 | "not cause the drones to move through each other" in {}
42 | }
43 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/cwinter/codecraft/core/objects/drone/DroneFactory.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.api.{BluePlayer, DroneSpec, Player, RedPlayer, TheGameMaster}
4 | import cwinter.codecraft.core.errors.Errors
5 | import cwinter.codecraft.core.game.{DroneWorldSimulator, GameConfig}
6 | import cwinter.codecraft.core.objects.IDGenerator
7 | import cwinter.codecraft.core.replay.{NullReplayRecorder, DummyDroneController}
8 | import cwinter.codecraft.graphics.engine.Debug
9 | import cwinter.codecraft.util.maths.{GlobalRNG, Rectangle, Vector2}
10 |
11 | object DroneFactory {
12 | val mockSimulator = new DroneWorldSimulator(
13 | TheGameMaster.defaultMap.createGameConfig(Seq.empty),
14 | forceReplayRecorder = Some(NullReplayRecorder)
15 | )
16 | val debug = new Debug
17 | val errors = new Errors(debug)
18 | val blueDroneContext = mockDroneContext(BluePlayer)
19 | val redDroneContext = mockDroneContext(RedPlayer)
20 |
21 | def mockDroneContext(player: Player): DroneContext = DroneContext(
22 | player,
23 | Rectangle(-100, 100, -100, 100),
24 | 1,
25 | None,
26 | None,
27 | new IDGenerator(player.id),
28 | GlobalRNG,
29 | true,
30 | false,
31 | mockSimulator,
32 | debug = debug,
33 | errors = errors
34 | )
35 |
36 | def blueDrone(spec: DroneSpec, position: Vector2): DroneImpl =
37 | new DroneImpl(spec, new DummyDroneController, blueDroneContext, position, 0)
38 |
39 | def redDrone(spec: DroneSpec, position: Vector2): DroneImpl =
40 | new DroneImpl(spec, new DummyDroneController, redDroneContext, position, 0)
41 | }
42 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/cwinter/codecraft/core/objects/drone/DroneModuleTestHelper.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.game.SimulatorEvent
4 | import cwinter.codecraft.util.maths.Vector2
5 |
6 | object DroneModuleTestHelper {
7 | def multipleUpdates(module: DroneModule, count: Int): (Seq[SimulatorEvent], Seq[Vector2], Seq[Vector2]) = {
8 | val allUpdates = for {
9 | _ <- 0 until count
10 | } yield module.update(0)
11 |
12 | allUpdates.foldLeft((Seq.empty[SimulatorEvent], Seq.empty[Vector2], Seq.empty[Vector2])){
13 | case ((es1, r1, rs1), (es2, r2, rs2)) => (es1 ++ es2, r1 ++ r2, rs1 ++ rs2)
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/cwinter/codecraft/core/objects/drone/MissileBatteryModuleTest.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.api.DroneSpec
4 | import cwinter.codecraft.core.game.SpawnHomingMissile
5 | import cwinter.codecraft.util.maths.Vector2
6 | import org.scalatest.FlatSpec
7 |
8 |
9 | class MissileBatteryModuleTest extends FlatSpec {
10 | val mockDroneSpec = DroneSpec(missileBatteries = 3, engines = 1)
11 | val mockDrone = DroneFactory.blueDrone(mockDroneSpec, Vector2(0, 0))
12 | val mockEnemy = DroneFactory.redDrone(DroneSpec(storageModules = 1), Vector2(100, 100))
13 |
14 | "A laser module" should "not generate spurious events" in {
15 | val missileBattery = new MissileBatteryModule(Seq(0, 1, 2), mockDrone)
16 | for {
17 | i <- 0 to 100
18 | event <- missileBattery.update(i)._1
19 | } fail()
20 | }
21 |
22 | it should "not consume resources" in {
23 | val lasers = new MissileBatteryModule(Seq(0, 1, 2), mockDrone)
24 | for {
25 | i <- 0 to 100
26 | (events, resources, resourcesSpawned) = lasers.update(i)
27 | } assert(resources == Seq())
28 | }
29 |
30 |
31 | it should "generate missile events exactly once after firing" in {
32 | val lasers = new MissileBatteryModule(Seq(0, 1, 2), mockDrone)
33 |
34 | assert(lasers.update(10) == ((Seq(), Seq(), Seq.empty[Vector2])))
35 |
36 | lasers.fire(mockEnemy)
37 | val (events, _, _) = lasers.update(10)
38 | assert(events.size == 3)
39 | assert(events.forall(_.isInstanceOf[SpawnHomingMissile]))
40 |
41 | val (events2, _, _) = lasers.update(10)
42 | assert(events2 == Seq())
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/cwinter/codecraft/core/objects/drone/ShieldGeneratorModuleTest.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.api.DroneSpec
4 | import cwinter.codecraft.util.maths.Vector2
5 | import org.scalatest.FlatSpec
6 |
7 | class ShieldGeneratorModuleTest extends FlatSpec {
8 | val mockDroneSpec = DroneSpec(5, missileBatteries = 2, shieldGenerators = 1, engines = 1)
9 | val mockDrone = DroneFactory.blueDrone(mockDroneSpec, Vector2(0, 0))
10 | val shieldGenerator = new ShieldGeneratorModule(Seq(2), mockDrone)
11 |
12 |
13 | "A shield generator module" should "absorb damage and loose hitpoints" in {
14 | val hitpointsBefore = shieldGenerator.currHitpoints
15 | val damage = 10
16 | val remainingDamage = shieldGenerator.absorbDamage(10)
17 | assert(remainingDamage < damage)
18 | assert(shieldGenerator.currHitpoints < hitpointsBefore)
19 | assert(shieldGenerator.currHitpoints + damage - remainingDamage == hitpointsBefore)
20 | }
21 |
22 |
23 | it should "regenerate all its hitpoints over time" in {
24 | DroneModuleTestHelper.multipleUpdates(shieldGenerator, 10000)
25 | assert(shieldGenerator.currHitpoints == shieldGenerator.maxHitpoints)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/cwinter/codecraft/core/replay/ReplayTest.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.replay
2 |
3 | import cwinter.codecraft.core.TestUtils
4 | import cwinter.codecraft.core.api.TheGameMaster
5 | import cwinter.codecraft.core.game.DroneWorldSimulator
6 | import org.scalatest.FlatSpec
7 |
8 | class ReplayTest extends FlatSpec {
9 | "A replayer" should "work" in {
10 | val timesteps = 5000
11 | val recorder = new StringReplayRecorder
12 | val simulator = new DroneWorldSimulator(
13 | TheGameMaster.defaultMap.createGameConfig(
14 | Seq(TheGameMaster.replicatorAI(), TheGameMaster.destroyerAI())),
15 | forceReplayRecorder = Some(recorder)
16 | )
17 | val canonical = TestUtils.runAndRecord(simulator, timesteps)
18 |
19 | val replaySimulator = TheGameMaster.createReplaySimulator(recorder.replayString.get)
20 | val fromReplay = TestUtils.runAndRecord(replaySimulator, timesteps)
21 |
22 | TestUtils.assertEqual(canonical, fromReplay, simulator, replaySimulator)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/PerformanceMonitor.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core
2 |
3 | import cwinter.codecraft.util.Stopwatch
4 |
5 |
6 | private[codecraft] trait PerformanceMonitor {
7 | def measure[T](name: Symbol)(code: => T): T
8 | def beginMeasurement(name: Symbol)
9 | def endMeasurement(name: Symbol)
10 | def compileReport: String
11 | }
12 |
13 | private[codecraft] class MockPerformanceMonitor extends PerformanceMonitor {
14 | override def measure[T](name: Symbol)(code: => T): T = code
15 | override def compileReport: String = "MockPerformanceMonitor does not perform any measurements."
16 | override def beginMeasurement(name: Symbol): Unit = ()
17 | override def endMeasurement(name: Symbol): Unit = ()
18 | }
19 |
20 | private[codecraft] class SimplePerformanceMonitor extends Stopwatch with PerformanceMonitor
21 |
22 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/basicplus/Destroyer.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.basicplus
2 |
3 | import cwinter.codecraft.util.maths.{GlobalRNG, RNG, Vector2}
4 |
5 | private[core] class Destroyer(val mothership: Mothership) extends BasicPlusController('Destroyer) {
6 | var attack = false
7 | var defend = false
8 |
9 | override def onSpawn(): Unit = {
10 | moveInDirection(Vector2(GlobalRNG.double(0, 100)))
11 | }
12 |
13 | override def onTick(): Unit = {
14 | handleWeapons()
15 |
16 | if (!defend && mothership.needsDefender) {
17 | mothership.registerDefender(this)
18 | defend = true
19 | }
20 | if (defend && mothership.allowsDefenderRelease) {
21 | mothership.unregisterDefender(this)
22 | defend = false
23 | }
24 | if (mothership.t % 600 == 0) attack = true
25 |
26 | if (enemies.nonEmpty) {
27 | val pClosest = closestEnemy.position
28 | if (canWin || defend) {
29 | moveInDirection(pClosest - position)
30 | } else {
31 | moveInDirection(position - pClosest)
32 | attack = false
33 | }
34 | } else if (defend) {
35 | if ((position - mothership.position).lengthSquared > 350 * 350) {
36 | moveTo(GlobalRNG.double(250, 350) * GlobalRNG.vector2() + mothership.position)
37 | }
38 | } else if (attack && mothership.lastCapitalShipSighting.isDefined) {
39 | for (p <- mothership.lastCapitalShipSighting)
40 | moveTo(p)
41 | } else if (GlobalRNG.bernoulli(0.005)) {
42 | moveTo(GlobalRNG.double(600, 900) * GlobalRNG.vector2() + mothership.position)
43 | }
44 | }
45 |
46 | override def onDeath(): Unit = {
47 | if (defend) mothership.unregisterDefender(this)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/basicplus/Hunter.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.basicplus
2 |
3 | import cwinter.codecraft.util.maths.GlobalRNG
4 |
5 |
6 | private[core] class Hunter(val mothership: Mothership) extends BasicPlusController('Hunter) {
7 | override def onTick(): Unit = {
8 | handleWeapons()
9 |
10 | if (enemies.nonEmpty) {
11 | val closest = closestEnemy
12 | if (closest.spec.missileBatteries > 0) {
13 | if (!canWin) {
14 | moveInDirection(position - closest.position)
15 | }
16 | } else {
17 | moveInDirection(closest.position - position)
18 | }
19 | }
20 |
21 | if (GlobalRNG.bernoulli(0.005)) {
22 | moveTo(0.9 * GlobalRNG.vector2(worldSize))
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/basicplus/ScoutingDroneController.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.basicplus
2 |
3 | import cwinter.codecraft.core.api.{Drone, MineralCrystal}
4 |
5 | private[core] class ScoutingDroneController(val mothership: Mothership) extends BasicPlusController('Harvester) {
6 | var hasReturned = false
7 | var nextCrystal: Option[MineralCrystal] = None
8 |
9 |
10 | override def onTick(): Unit = {
11 | if (nextCrystal.exists(_.harvested)) nextCrystal = None
12 | if (nextCrystal.isEmpty && availableStorage > 0) nextCrystal = mothership.findClosestMineral(position)
13 |
14 | if (enemies.nonEmpty && closestEnemy.spec.missileBatteries > 0) {
15 | moveInDirection(position - closestEnemy.position)
16 | } else {
17 | if (availableStorage <= 0 && !hasReturned) {
18 | moveTo(mothership)
19 | nextCrystal.foreach(mothership.abortHarvestingMission)
20 | nextCrystal = None
21 | } else if (hasReturned && availableStorage > 0 || (nextCrystal.isEmpty && !isHarvesting)) {
22 | hasReturned = false
23 | scout()
24 | } else {
25 | for (
26 | c <- nextCrystal
27 | if !isHarvesting
28 | ) moveTo(c)
29 | }
30 | }
31 | }
32 |
33 | override def onArrivesAtMineral(mineral: MineralCrystal): Unit = {
34 | harvest(mineral)
35 | }
36 |
37 | override def onArrivesAtDrone(drone: Drone): Unit = {
38 | giveResourcesTo(drone)
39 | hasReturned = true
40 | }
41 |
42 | override def onDeath(): Unit = {
43 | for (m <- nextCrystal)
44 | mothership.abortHarvestingMission(m)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/basicplus/SearchToken.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.basicplus
2 |
3 | import cwinter.codecraft.core.api._
4 | import cwinter.codecraft.util.maths.Vector2
5 | import GameConstants.DroneVisionRange
6 |
7 |
8 | private[basicplus] case class SearchToken(x: Int, y: Int) {
9 | val pos: Vector2 = Vector2((x + 0.5) * DroneVisionRange, (y + 0.5) * DroneVisionRange)
10 | }
11 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/cheese/Destroyer.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.cheese
2 |
3 | import cwinter.codecraft.core.api.{Drone, MineralCrystal, DroneController}
4 | import cwinter.codecraft.util.maths.Vector2
5 |
6 | private[core] class Destroyer(targetPos: Vector2) extends DroneController {
7 | override def onSpawn(): Unit = {
8 | moveTo(targetPos)
9 | }
10 | override def onMineralEntersVision(mineralCrystal: MineralCrystal): Unit = ()
11 | override def onTick(): Unit = {
12 | for (d <- dronesInSight.find(d => d.isEnemy && isInMissileRange(d))) {
13 | fireMissilesAt(d)
14 | }
15 | }
16 | override def onArrivesAtPosition(): Unit = ()
17 | override def onDeath(): Unit = ()
18 | override def onDroneEntersVision(drone: Drone): Unit = ()
19 | }
20 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/cheese/Mothership.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.cheese
2 |
3 | import cwinter.codecraft.core.api.{Drone, DroneController, DroneSpec, MineralCrystal}
4 |
5 |
6 | private[core] class Mothership extends DroneController {
7 | final val destroyerSpec = DroneSpec(0, 3, 0, 0, 1)
8 |
9 | override def onSpawn(): Unit = {
10 | buildDrone(new Destroyer(-position), destroyerSpec)
11 | moveTo(-position)
12 | }
13 | override def onMineralEntersVision(mineralCrystal: MineralCrystal): Unit = ()
14 | override def onTick(): Unit = ()
15 | override def onArrivesAtPosition(): Unit = ()
16 | override def onDeath(): Unit = ()
17 | override def onDroneEntersVision(drone: Drone): Unit = ()
18 | }
19 |
20 |
21 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/destroyer/DestroyerController.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.destroyer
2 |
3 | import cwinter.codecraft.core.ai.shared.AugmentedController
4 |
5 |
6 | private[codecraft] class DestroyerController(
7 | _context: DestroyerContext
8 | ) extends AugmentedController[DestroyerCommand, DestroyerContext](_context)
9 |
10 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/destroyer/Scout.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.destroyer
2 |
3 | import cwinter.codecraft.core.api.MineralCrystal
4 | import cwinter.codecraft.util.maths.Vector2
5 |
6 |
7 | private[codecraft] class Scout(ctx: DestroyerContext) extends DestroyerController(ctx) {
8 | var hasReturned = false
9 | var nextCrystal: Option[MineralCrystal] = None
10 | var flightTimer = 0
11 | var afraid = 0
12 |
13 | override def onTick(): Unit = {
14 | flightTimer -= tickPeriod
15 | afraid -= tickPeriod
16 | if (flightTimer == 0) halt()
17 |
18 | if (flightTimer <= 0) {
19 | scout()
20 | if (searchToken.isEmpty) scoutRandomly()
21 | avoidThreats()
22 | }
23 |
24 | handleWeapons()
25 | }
26 |
27 | def scoutRandomly(): Unit = {
28 | import context.rng
29 | if (!isMoving) {
30 | moveTo(Vector2(
31 | rng.double() * (worldSize.xMax - worldSize.xMin) + worldSize.xMin,
32 | rng.double() * (worldSize.yMax - worldSize.yMin) + worldSize.yMin
33 | ))
34 | }
35 | }
36 |
37 | def avoidThreats(): Unit = {
38 | val threats = enemies.filter(_.spec.missileBatteries > 0)
39 | if (threats.nonEmpty) {
40 | val (closest, dist2) = {
41 | for (threat <- threats)
42 | yield (threat, (threat.position - position).lengthSquared)
43 | }.minBy(_._2)
44 |
45 | if (dist2 < 330 * 330) {
46 | moveInDirection(position - closest.position)
47 | if (afraid > 0) flightTimer = 60
48 | else flightTimer = 30
49 | afraid = 90
50 | for (n <- searchToken) {
51 | context.searchCoordinator.dangerous(n)
52 | searchToken = None
53 | }
54 | }
55 | }
56 | }
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/MothershipCoordinator.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator
2 |
3 |
4 | private[codecraft] class MothershipCoordinator {
5 | private var orphanedHarvesters = List.empty[Harvester]
6 | private var _motherships = Set.empty[Replicator]
7 | def motherships = _motherships
8 |
9 | def online(mothership: Replicator): Unit = {
10 | _motherships += mothership
11 | }
12 |
13 | def offline(mothership: Replicator): Unit = {
14 | _motherships -= mothership
15 | }
16 |
17 | def registerOrphan(harvester: Harvester): Unit = {
18 | orphanedHarvesters ::= harvester
19 | }
20 |
21 | def stuck(replicator: Replicator): Unit = {
22 | for {
23 | m <- _motherships.find(_.hasSpareSlave)
24 | s <- m.relieveSlave()
25 | } s.assignNewMaster(replicator)
26 | }
27 |
28 | def requestHarvester(replicator: Replicator): Unit = {
29 | if (orphanedHarvesters.nonEmpty) {
30 | val harvester = orphanedHarvesters.head
31 | harvester.assignNewMaster(replicator)
32 | orphanedHarvesters = orphanedHarvesters.tail
33 | } else {
34 | for {
35 | m <- _motherships.find(_.hasPlentySlaves)
36 | s <- m.relieveSlave()
37 | } s.assignNewMaster(replicator)
38 | }
39 | }
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/ReplicatorContext.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator
2 |
3 | import cwinter.codecraft.core.ai.replicator.combat.{ReplicatorCommand, ReplicatorBattleCoordinator}
4 | import cwinter.codecraft.core.ai.shared.{HarvestCoordinatorWithZones, SharedContext}
5 |
6 |
7 | private[codecraft] class ReplicatorContext(
8 | val greedy: Boolean,
9 | val confident: Boolean,
10 | val aggressive: Boolean
11 | ) extends SharedContext[ReplicatorCommand] {
12 | val battleCoordinator = new ReplicatorBattleCoordinator(this)
13 | val mothershipCoordinator = new MothershipCoordinator
14 | val harvestCoordinator = new HarvestCoordinatorWithZones
15 |
16 | var isReplicatorInConstruction: Boolean = false
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/ReplicatorController.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator
2 |
3 | import cwinter.codecraft.core.ai.replicator.combat.ReplicatorCommand
4 | import cwinter.codecraft.core.ai.shared.AugmentedController
5 | import cwinter.codecraft.core.api.Drone
6 |
7 |
8 | private[codecraft] class ReplicatorController(_context: ReplicatorContext)
9 | extends AugmentedController[ReplicatorCommand, ReplicatorContext](_context) {
10 | override def onDroneEntersVision(drone: Drone): Unit = {
11 | super.onDroneEntersVision(drone)
12 | if (drone.isEnemy && drone.spec.missileBatteries > 0)
13 | context.battleCoordinator.foundArmedEnemy(drone)
14 | }
15 |
16 |
17 | def normalizedEnemyCount: Double =
18 | Util.approximateStrength(armedEnemies)
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/TargetAcquisition.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator
2 |
3 | import cwinter.codecraft.core.api.Drone
4 |
5 |
6 | private[codecraft] trait TargetAcquisition {
7 | self: ReplicatorController =>
8 |
9 | val normalizedStrength: Double
10 |
11 | private[this] var _target = Option.empty[Drone]
12 | private[this] var _attack = Option.empty[Drone]
13 |
14 | def maybeClosest = _target
15 | def maybeClosest_=(value: Option[Drone]): Unit = {
16 | if (_target != value) {
17 | for (t <- _target) self.context.battleCoordinator.notTargeting(t, this)
18 | for (t <- value) self.context.battleCoordinator.targeting(t, this)
19 | _target = value
20 | }
21 | }
22 |
23 | def isCommited = _attack.exists(!_.isDead)
24 | def attack(enemy: Drone): Unit = _attack = Some(enemy)
25 | }
26 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/Util.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator
2 |
3 | import cwinter.codecraft.core.api.Drone
4 |
5 |
6 | private[codecraft] object Util {
7 | final val SoldierStrength = 2
8 |
9 |
10 | def approximateStrength(drone: Drone): Double = {
11 | val hitpoints = if (drone.isVisible) drone.hitpoints else drone.spec.maxHitpoints
12 | val attack = drone.spec.missileBatteries
13 | val strength = math.sqrt(attack * hitpoints)
14 | // 2 * the number of Soldier drones required to counter the shield regeneration
15 | val shieldRegenBonus = 2 * 0.3 * drone.spec.shieldGenerators
16 | // accounts for the fact that a larger drone has an advantage against several
17 | // smaller ones, since those will start to die off, decreasing the dps
18 | val sizeBonus = math.sqrt(strength / (0.5 * strength + 1))
19 | (strength * sizeBonus + shieldRegenBonus) / SoldierStrength
20 | }
21 |
22 | def approximateStrength(drones: Iterable[Drone]): Double =
23 | drones.foldLeft(0.0)((acc, d) => acc + approximateStrength(d))
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/combat/AssaultCapitalShip.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator.combat
2 |
3 | import cwinter.codecraft.core.ai.replicator.Util
4 | import cwinter.codecraft.core.ai.shared.Mission
5 | import cwinter.codecraft.core.api.Drone
6 |
7 |
8 | private[codecraft] class AssaultCapitalShip(enemy: Drone, context: ReplicatorBattleCoordinator)
9 | extends Mission[ReplicatorCommand] {
10 | var minRequired = computeMinRequired
11 | def maxRequired = minRequired * 2
12 | val priority = 10
13 | var maxDist2: Option[Double] = None
14 |
15 | def locationPreference = Some(enemy.lastKnownPosition)
16 |
17 | private var searchRadius = 0.0
18 |
19 | def missionInstructions: ReplicatorCommand =
20 | if (enemy.isVisible || searchRadius == 0) Attack(maxDist2.getOrElse(0), enemy, notFound)
21 | else Search(enemy.lastKnownPosition, searchRadius)
22 |
23 | def notFound(): Unit = searchRadius = 750
24 |
25 | override def update(): Unit = {
26 | if (!enemy.isVisible && nAssigned > 0 && searchRadius > 0) searchRadius += 1
27 | else if (enemy.isVisible) searchRadius = 0
28 |
29 | maxDist2 =
30 | if (nAssigned == 0 || context.context.confident) None
31 | else {
32 | val sortedByDist =
33 | assigned.toSeq.sortBy(d => (enemy.lastKnownPosition - d.position).lengthSquared)
34 | val straggler = sortedByDist(math.min(nAssigned - 1, minRequired))
35 | Some((enemy.lastKnownPosition - straggler.position).length)
36 | }
37 | }
38 |
39 | def computeMinRequired: Int = math.ceil(
40 | if (context.clusters.contains(enemy)) context.clusters(enemy).strength
41 | else Util.approximateStrength(enemy)
42 | ).toInt
43 |
44 | def hasExpired = enemy.isDead
45 | }
46 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/combat/Assist.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator.combat
2 |
3 | import cwinter.codecraft.core.ai.replicator.ReplicatorController
4 | import cwinter.codecraft.core.ai.shared.Mission
5 | import cwinter.codecraft.core.api.Drone
6 |
7 |
8 | private[codecraft] class Assist(
9 | val friend: ReplicatorController,
10 | val priority: Int,
11 | val minRequired: Int,
12 | radius: Int
13 | ) extends Mission[ReplicatorCommand] {
14 | val radius2 = radius * radius
15 | var timeout = 10
16 |
17 | val maxRequired = 3 * minRequired
18 | val locationPreference = None
19 |
20 | def missionInstructions = AttackMove(
21 | if (friend.enemies.nonEmpty) friend.closestEnemy.lastKnownPosition
22 | else friend.position
23 | )
24 | def hasExpired = friend.isDead || timeout <= 0
25 | override def update(): Unit = timeout -= friend.tickPeriod
26 | override def candidateFilter(drone: Drone): Boolean =
27 | drone != friend &&
28 | (drone.position - friend.position).lengthSquared <= radius2
29 |
30 | def refresh(): Unit = timeout = 10
31 | }
32 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/combat/EliminateEnemy.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator.combat
2 |
3 | import cwinter.codecraft.core.ai.replicator.{ReplicatorContext, Util}
4 | import cwinter.codecraft.core.ai.shared.Mission
5 | import cwinter.codecraft.core.api.Drone
6 |
7 |
8 | private[codecraft] class EliminateEnemy(
9 | val enemy: Drone,
10 | val context: ReplicatorContext
11 | ) extends Mission[ReplicatorCommand] {
12 | var priority: Int = 3
13 | var minRequired = computeMinRequired
14 | def maxRequired = minRequired * 2
15 |
16 | def locationPreference = Some(enemy.lastKnownPosition)
17 |
18 | val missionInstructions: ReplicatorCommand = Attack(0, enemy, notFound)
19 |
20 | def notFound(): Unit = deactivate()
21 |
22 | override def update(): Unit = {
23 | if (isDeactivated && enemy.isVisible)
24 | reactivate()
25 | computePriority()
26 | val newMinRequired = computeMinRequired
27 | if (minRequired < newMinRequired) {
28 | minRequired = newMinRequired
29 | if (nAssigned < newMinRequired) disband()
30 | }
31 | }
32 |
33 | def computePriority(): Unit = {
34 | val motherships = context.mothershipCoordinator.motherships
35 | if (motherships.nonEmpty) {
36 | val closest = motherships.minBy(x => (x.position - enemy.lastKnownPosition).lengthSquared)
37 | val dist = (closest.position - enemy.lastKnownPosition).length
38 | priority = math.min(9, math.max(3, 9 - ((dist - 1000) / 250).toInt))
39 | }
40 | }
41 |
42 | def computeMinRequired: Int = math.ceil(
43 | if (clusters.contains(enemy)) clusters(enemy).strength
44 | else Util.approximateStrength(enemy)
45 | ).toInt
46 |
47 | def clusters = context.battleCoordinator.clusters
48 |
49 | def hasExpired = enemy.isDead
50 | }
51 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/combat/Guard.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator.combat
2 |
3 | import cwinter.codecraft.core.ai.replicator.ReplicatorController
4 | import cwinter.codecraft.core.ai.shared.Mission
5 |
6 |
7 | private[codecraft] class Guard(
8 | val friend: ReplicatorController,
9 | var minRequired: Int
10 | ) extends Mission[ReplicatorCommand] {
11 | val priority = 10
12 | private var timeout = 0
13 | resetTimeout()
14 |
15 | def maxRequired = (minRequired * 1.5f).toInt
16 |
17 | def locationPreference = Some(friend.position)
18 |
19 | def missionInstructions = Circle(friend.position, 450)
20 | def hasExpired = maxRequired == 0 || friend.isDead
21 | override def update(): Unit = {
22 | timeout -= friend.tickPeriod
23 | if (timeout <= 0) {
24 | minRequired -= 1
25 | reduceAssignedToMax()
26 | resetTimeout()
27 | }
28 | }
29 |
30 | private def resetTimeout(): Unit = timeout = 600
31 |
32 | def refresh(min: Int): Unit = {
33 | if (min > minRequired) minRequired = min
34 | else if (min + 1 >= minRequired) resetTimeout()
35 | }
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/combat/KeepEyeOnEnemy.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator.combat
2 |
3 | import cwinter.codecraft.core.ai.shared.Mission
4 | import cwinter.codecraft.core.api.Drone
5 |
6 |
7 | private[codecraft] class KeepEyeOnEnemy(enemy: Drone) extends Mission[ReplicatorCommand] {
8 | val minRequired = 1
9 | val maxRequired = 1
10 | val priority = 2
11 |
12 | def locationPreference = Some(enemy.lastKnownPosition)
13 |
14 | val missionInstructions = Observe(enemy, notFound)
15 |
16 | def notFound(): Unit = deactivate()
17 |
18 | override def update(): Unit = if (isDeactivated && enemy.isVisible) reactivate()
19 |
20 | def hasExpired: Boolean = enemy.isDead
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/combat/ReplicatorCommand.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator.combat
2 |
3 | import cwinter.codecraft.core.api.Drone
4 | import cwinter.codecraft.util.maths.Vector2
5 |
6 |
7 | private[codecraft] sealed trait ReplicatorCommand
8 |
9 | private[codecraft] case object Scout extends ReplicatorCommand
10 | private[codecraft] case class Attack(maxDist: Double, enemy: Drone, notFound: () => Unit) extends ReplicatorCommand
11 | private[codecraft] case class Search(position: Vector2, radius: Double) extends ReplicatorCommand
12 | private[codecraft] case class AttackMove(position: Vector2) extends ReplicatorCommand
13 | private[codecraft] case class Circle(position: Vector2, radius: Double) extends ReplicatorCommand
14 | private[codecraft] case class Observe(enemy: Drone, notFound: () => Unit) extends ReplicatorCommand
15 |
16 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/replicator/combat/ScoutingMission.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.replicator.combat
2 |
3 | import cwinter.codecraft.core.ai.shared.Mission
4 |
5 | private[codecraft] class ScoutingMission extends Mission[ReplicatorCommand] {
6 | val minRequired = 1
7 | val maxRequired = Int.MaxValue
8 | val missionInstructions = Scout
9 | val priority = 1
10 | val hasExpired = false
11 | val locationPreference = None
12 | }
13 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/shared/BasicHarvestCoordinator.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.shared
2 |
3 | import cwinter.codecraft.core.api.MineralCrystal
4 | import cwinter.codecraft.util.maths.Vector2
5 |
6 |
7 | private[codecraft] class BasicHarvestCoordinator {
8 | private var _minerals = Set.empty[MineralCrystal]
9 | def minerals = _minerals
10 | private var claimedMinerals = Set.empty[MineralCrystal]
11 |
12 | def findClosestMineral(position: Vector2): Option[MineralCrystal] = {
13 | closestUnclaimedMineral(position, _minerals)
14 | }
15 |
16 | protected def closestUnclaimedMineral(position: Vector2, eligible: Set[MineralCrystal]): Option[MineralCrystal] = {
17 | val filtered =
18 | for (
19 | m <- _minerals -- claimedMinerals
20 | ) yield m
21 | val result =
22 | if (filtered.isEmpty) None
23 | else Some(filtered.minBy(m => (m.position - position).lengthSquared))
24 | for (m <- result) {
25 | claimedMinerals += m
26 | }
27 | result
28 | }
29 |
30 | def registerMineral(mineralCrystal: MineralCrystal): Unit = {
31 | _minerals += mineralCrystal
32 | }
33 |
34 | def abortHarvestingMission(mineralCrystal: MineralCrystal): Unit = {
35 | claimedMinerals -= mineralCrystal
36 | }
37 |
38 | def update(): Unit = {
39 | _minerals = _minerals.filter(!_.harvested)
40 | }
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/shared/BattleCoordinator.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.shared
2 |
3 | import cwinter.codecraft.core.api.{DroneControllerBase, Drone}
4 |
5 |
6 | private[codecraft] trait BattleCoordinator[TCommand] {
7 | type Executor = DroneControllerBase with MissionExecutor[TCommand]
8 | private[this] var _enemyCapitalShips = Set.empty[Drone]
9 | protected var _missions = List.empty[Mission[TCommand]]
10 | private[this] var executors = Set.empty[Executor]
11 |
12 |
13 | def update(): Unit = {
14 | _missions.foreach(_.update())
15 | purgeExpiredMissions()
16 | reallocateWarriors()
17 | _enemyCapitalShips = _enemyCapitalShips.filter(!_.isDead)
18 | }
19 |
20 | def purgeExpiredMissions(): Unit = {
21 | val (expired, active) = _missions.partition(_.hasExpired)
22 | for (m <- expired) m.disband()
23 | _missions = active
24 | }
25 |
26 | def reallocateWarriors(): Unit = {
27 | for (m <- _missions) {
28 | val recruits = m.findSuitableRecruits(executors)
29 | for (r <- recruits)
30 | r.abortMission()
31 | for (r <- recruits)
32 | r.startMission(m)
33 | }
34 | }
35 |
36 | def foundCapitalShip(drone: Drone): Unit = {
37 | if (!_enemyCapitalShips.contains(drone)) {
38 | _enemyCapitalShips += drone
39 | }
40 | }
41 |
42 | def online(hunter: Executor): Unit =
43 | executors += hunter
44 |
45 | def offline(hunter: Executor): Unit = {
46 | executors -= hunter
47 | }
48 |
49 | def addMission(mission: Mission[TCommand]): Unit = _missions ::= mission
50 |
51 | def enemyCapitalShips: Set[Drone] = _enemyCapitalShips
52 | }
53 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/shared/DroneCounter.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.shared
2 |
3 | private[codecraft] class DroneCounter {
4 | private[this] var counts = Map.empty[Class[_], Int]
5 |
6 | def apply[T](clazz: Class[T]): Int = counts.getOrElse(clazz, 0)
7 |
8 | def increment[T](clazz: Class[T]): Unit =
9 | counts = counts.updated(clazz, this(clazz) + 1)
10 |
11 | def decrement[T](clazz: Class[T]): Unit =
12 | counts = counts.updated(clazz, this(clazz) - 1)
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/shared/SearchCoordinator.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.shared
2 |
3 | import cwinter.codecraft.core.api.GameConstants.DroneVisionRange
4 | import cwinter.codecraft.util.maths.{Rectangle, Vector2}
5 |
6 | import scala.collection.mutable
7 |
8 |
9 | private[codecraft] class SearchCoordinator(worldSize: Rectangle) {
10 | private var searchTokens: Set[SearchToken] = genSearchTokens
11 | private val dangerousSearchTokens = mutable.Queue.empty[SearchToken]
12 | private var cooldown = 300
13 |
14 | private def genSearchTokens: Set[SearchToken] = {
15 | val width = math.ceil(worldSize.width / DroneVisionRange).toInt
16 | val height = math.ceil(worldSize.height / DroneVisionRange).toInt
17 | val xOffset = (worldSize.width / DroneVisionRange / 2).toInt
18 | val yOffset = (worldSize.height / DroneVisionRange / 2).toInt
19 | val tokens = Seq.tabulate(width, height){
20 | (x, y) => SearchToken(x - xOffset, y - yOffset)
21 | }
22 | for (ts <- tokens; t <- ts) yield t
23 | }.toSet
24 |
25 |
26 | def getSearchToken(pos: Vector2): Option[SearchToken] = {
27 | if (searchTokens.isEmpty) {
28 | None
29 | } else {
30 | val closest = searchTokens.minBy(t => (t.pos - pos).lengthSquared)
31 | searchTokens -= closest
32 | Some(closest)
33 | }
34 | }
35 |
36 | def dangerous(searchToken: SearchToken): Unit = {
37 | dangerousSearchTokens.enqueue(searchToken)
38 | }
39 |
40 | def returnSearchToken(searchToken: SearchToken): Unit = {
41 | searchTokens += searchToken
42 | }
43 |
44 | def update(): Unit = {
45 | if (dangerousSearchTokens.nonEmpty) {
46 | cooldown -= 1
47 | if (cooldown <= 0) {
48 | cooldown = 300
49 | searchTokens += dangerousSearchTokens.dequeue()
50 | }
51 | }
52 | }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/shared/SearchToken.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.shared
2 |
3 | import cwinter.codecraft.core.api.GameConstants.DroneVisionRange
4 | import cwinter.codecraft.util.maths.Vector2
5 |
6 |
7 | private[codecraft] case class SearchToken(x: Int, y: Int) {
8 | val pos: Vector2 = Vector2((x + 0.5) * DroneVisionRange, (y + 0.5) * DroneVisionRange)
9 | }
10 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/ai/shared/SharedContext.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.ai.shared
2 |
3 | import cwinter.codecraft.core.api.MetaController
4 | import cwinter.codecraft.util.maths.{RNG, Rectangle}
5 |
6 | import scala.util.Random
7 |
8 |
9 | private[codecraft] trait SharedContext[TCommand] extends MetaController {
10 | val rng = new RNG(0)
11 | val droneCount = new DroneCounter
12 |
13 | private[this] lazy val _searchCoordinator: SearchCoordinator = new SearchCoordinator(worldSize)
14 | def searchCoordinator = {
15 | require(_searchCoordinator != null, "Context is uninitialised.")
16 | _searchCoordinator
17 | }
18 |
19 | def harvestCoordinator: BasicHarvestCoordinator
20 | def battleCoordinator: BattleCoordinator[TCommand]
21 |
22 | override def onTick(): Unit = {
23 | harvestCoordinator.update()
24 | battleCoordinator.update()
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/api/CodeCraftException.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.api
2 |
3 | /** Base class for all exceptions specific to CodeCraft. */
4 | abstract class CodeCraftException(message: String) extends Exception(message)
5 |
6 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/api/DroneController.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.api
2 |
3 | // text duplicated in BaseDroneController and JDroneController
4 | /** A drone controller is an object that governs the behaviour of a drone.
5 | * It exposes a wide range of methods to query the underlying drone's state and give it commands.
6 | * You can inherit from this class to and override the `onEvent` methods to implement a
7 | * drone controller with custom behaviour.
8 | *
9 | * In Java, use [[JDroneController]] instead.
10 | */
11 | class DroneController extends DroneControllerBase {
12 | /** Returns an empty Seq. */
13 | @deprecated("Drones do not store mineral crystals anymore, only resources.", "0.2.4.0")
14 | def storedMinerals: Seq[MineralCrystal] = Seq.empty
15 |
16 | /** Gets all drones currently within the sight radius of this drone. */
17 | def dronesInSight: Set[Drone] = super.dronesInSightScala
18 |
19 | /** Gets all enemy drones currently within the sight radius of this drone. */
20 | def enemiesInSight: Set[Drone] = super.enemiesInSightScala
21 |
22 | /** Gets all allied drones currently within the sight radius of this drone. */
23 | def alliesInSight: Set[Drone] = super.alliesInSightScala
24 |
25 | /** Gets all mineral crystals currently within the sight radius of this drone. */
26 | def mineralsInSight: Set[MineralCrystal] = super.mineralsInSightScala
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/api/GameConstants.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.api
2 |
3 | object GameConstants {
4 | /** The largest distance at which two drones can see each other. */
5 | final val DroneVisionRange = 500
6 |
7 | /** The largest distance at which homing missiles can be fired at another drone. */
8 | final val MissileLockOnRange = 300
9 |
10 | /** The number of timesteps that have to elapse before homing missiles can be fired again. */
11 | final val MissileCooldown = 30
12 |
13 | /** The number of timesteps it takes for a missile to disappear after being fired. */
14 | final val MissileLifetime = 50
15 |
16 | /** The speed of missiles measured in units distance per timestep. */
17 | final val MissileSpeed = 17
18 |
19 | /** The number of timesteps it takes for shield hitpoints to increase by one (per shield generator module). */
20 | final val ShieldRegenerationInterval = 100
21 |
22 | /** The amount of hitpoints provided per shield generator module. */
23 | final val ShieldMaximumHitpoints = 7
24 |
25 | /** The largest distance at which minerals can be harvested. */
26 | final val HarvestingRange = 70
27 |
28 | /** The number of timesteps it takes to build a drone is `DroneConstructionTime` *
29 | * (number of modules of the drone being built) / (number constructor modules of the building drone).
30 | */
31 | final val DroneConstructionTime = 100
32 |
33 | /** The amount of resources required to build a */
34 | final val ModuleResourceCost = 5
35 |
36 | /** The number of timesteps it takes to harvest 1 resource from a mineral crystal. */
37 | final val HarvestingInterval = 60
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/api/JDroneController.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.api
2 |
3 | import scala.collection.JavaConverters._
4 |
5 | // text duplicated in DroneControllerBase and DroneController
6 | /** A drone controller is an object that governs the behaviour of a drone.
7 | * It exposes a wide range of methods to query the underlying drone's state and give it commands.
8 | * You can inherit from this class and override the `onEvent` methods to implement a
9 | * drone controller with custom behaviour.
10 | *
11 | * In Scala, use [[DroneController]] instead.
12 | */
13 | class JDroneController extends DroneControllerBase {
14 | /** Returns an empty list. */
15 | @deprecated("Drones do not store mineral crystals anymore, only resources.", "0.2.4.0")
16 | def storedMinerals: java.util.List[MineralCrystal] = new java.util.ArrayList()
17 |
18 | /** Gets all drones currently within the sight radius of this drone. */
19 | def dronesInSight: java.util.Set[Drone] = super.dronesInSightScala.asJava
20 |
21 | /** Gets all drones currently within the sight radius of this drone. */
22 | def enemiesInSight: java.util.Set[Drone] = super.enemiesInSightScala.asJava
23 |
24 | /** Gets all drones currently within the sight radius of this drone. */
25 | def alliesInSight: java.util.Set[Drone] = super.alliesInSightScala.asJava
26 |
27 | /** Gets all mineral crystals currently within the sight radius of this drone. */
28 | def mineralsInSight: java.util.Set[MineralCrystal] = super.mineralsInSightScala.asJava
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/api/MetaController.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.api
2 |
3 | import cwinter.codecraft.core.game.SimulationContext
4 | import cwinter.codecraft.util.maths.Rectangle
5 |
6 | /** In addition to your [[DroneController]]s you can have one [[MetaController]], which has a method
7 | * that will be called once every tick before the onEvent methods on your drone controllers are called.
8 | * This can be useful if you want to perform some global computation once every timestep.
9 | * You can instantiate your [[MetaController]] using the [[DroneControllerBase.metaController]] method.
10 | */
11 | trait MetaController {
12 | private[core] var _worldSize: Rectangle = _
13 | private[core] var _tickPeriod: Int = -1
14 | private[core] implicit var _simulationContext: SimulationContext = _
15 |
16 | def onTick(): Unit
17 | def gameOver(winner: Player): Unit = ()
18 | def init(): Unit = ()
19 |
20 | private[codecraft] def onTick(simulationContext: SimulationContext): Unit = {
21 | _simulationContext = simulationContext
22 | onTick()
23 | }
24 |
25 | def worldSize: Rectangle = {
26 | require(_worldSize != null, cantAccessYet("worldSize"))
27 | _worldSize
28 | }
29 |
30 | def tickPeriod: Int = {
31 | require(_tickPeriod != -1, cantAccessYet("tickPeriod"))
32 | _tickPeriod
33 | }
34 |
35 | private def cantAccessYet(property: String): String =
36 | s"`$property` is only available after the game has started. " +
37 | s"If you have any initialisation code that relies on `$property`, make it lazy or override the `init()` method and put it there."
38 | }
39 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/api/MineralCrystal.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.api
2 |
3 | import cwinter.codecraft.core.objects.MineralCrystalImpl
4 | import cwinter.codecraft.util.maths.Vector2
5 |
6 | import scala.scalajs.js.annotation.JSExportAll
7 |
8 |
9 | /** A mineral crystal.
10 | * Can be harvested by drones with storage modules to obtain resources.
11 | */
12 | @JSExportAll
13 | class MineralCrystal(
14 | private[core] val mineralCrystal: MineralCrystalImpl,
15 | private[core] val holder: Player
16 | ) {
17 | def position: Vector2 = mineralCrystal.position
18 | def size: Int = mineralCrystal.size
19 | def harvested: Boolean = mineralCrystal.harvested
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/api/ObjectNotVisibleException.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.api
2 |
3 | class ObjectNotVisibleException(message: String) extends CodeCraftException(message)
4 |
5 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/errors/ErrorMessageObject.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.errors
2 |
3 | import cwinter.codecraft.graphics.engine.{TextModel, Debug}
4 | import cwinter.codecraft.util.maths.{ColorRGBA, Vector2}
5 |
6 | private[core] class ErrorMessageObject(
7 | val message: String,
8 | val errorLevel: ErrorLevel,
9 | var position: Vector2,
10 | val lifetime: Int = 120
11 | ) {
12 | private[this] var age = 0
13 |
14 | def update(): Unit = {
15 | position += Vector2(0, 0.66f)
16 | age += 1
17 | }
18 |
19 | def model: TextModel = {
20 | val color = ColorRGBA(errorLevel.color, 1 - (age.toFloat * age / (lifetime * lifetime)))
21 | TextModel(message, position.x, position.y, color)
22 | }
23 |
24 | def hasFaded: Boolean = age >= lifetime
25 | }
26 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/errors/Errors.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.errors
2 |
3 | import cwinter.codecraft.core.api.CodeCraftException
4 | import cwinter.codecraft.graphics.engine.Debug
5 | import cwinter.codecraft.util.maths.{ColorRGB, Vector2}
6 |
7 | private[codecraft] class Errors(debug: Debug) {
8 | private[this] var errorMessages = List.empty[ErrorMessageObject]
9 |
10 | def error(exception: CodeCraftException, position: Vector2): Nothing = {
11 | println(exception)
12 | exception.printStackTrace()
13 | addMessage(exception.getMessage, position, Error)
14 | throw exception
15 | }
16 |
17 | def warn(message: String, position: Vector2): Unit = {
18 | addMessage(message, position, Warning)
19 | }
20 |
21 | def inform(message: String, position: Vector2): Unit = {
22 | addMessage(message, position, Information)
23 | }
24 |
25 | def addMessage(message: String, position: Vector2, errorLevel: ErrorLevel): Unit = {
26 | errorMessages ::= new ErrorMessageObject(message, errorLevel, position)
27 | }
28 |
29 | def updateMessages(): Unit = {
30 | errorMessages =
31 | for (
32 | m <- errorMessages
33 | if !m.hasFaded
34 | ) yield {
35 | m.update()
36 | debug.drawText(m.model)
37 | m
38 | }
39 | }
40 | }
41 |
42 |
43 | private[codecraft] sealed trait ErrorLevel {
44 | val color: ColorRGB
45 | val messagePrefix: String
46 | }
47 |
48 | private[codecraft] case object Error extends ErrorLevel {
49 | val color = ColorRGB(1, 0, 0)
50 | val messagePrefix = "Error: "
51 | }
52 |
53 | private[codecraft] case object Warning extends ErrorLevel {
54 | val color = ColorRGB(1, 0.5f, 0)
55 | val messagePrefix = "Warning: "
56 | }
57 |
58 | private[codecraft] case object Information extends ErrorLevel {
59 | val color = ColorRGB(0, 0, 1)
60 | val messagePrefix = "Note: "
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/game/CommandRecorder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.game
2 |
3 | import cwinter.codecraft.core.objects.drone.DroneCommand
4 |
5 | import scala.collection.mutable.ListBuffer
6 |
7 |
8 | private[core] class CommandRecorder {
9 | private val commands = ListBuffer.empty[(Int, DroneCommand)]
10 |
11 | def record(droneID: Int, droneCommand: DroneCommand): Unit = {
12 | commands.append((droneID, droneCommand))
13 | }
14 |
15 | def popAll(): Seq[(Int, DroneCommand)] = {
16 | val result = commands.toList
17 | commands.clear()
18 | result
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/game/GameConfig.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.game
2 |
3 | import cwinter.codecraft.core.api.DroneControllerBase
4 | import cwinter.codecraft.core.objects.MineralCrystalImpl
5 | import cwinter.codecraft.util.maths.Rectangle
6 |
7 | /** Aggregates all pieces of information required to start a game. */
8 | private[core] case class GameConfig(
9 | worldSize: Rectangle,
10 | minerals: Seq[MineralSpawn],
11 | drones: Seq[(Spawn, DroneControllerBase)],
12 | winConditions: Seq[WinCondition],
13 | tickPeriod: Int,
14 | rngSeed: Int
15 | ) {
16 | def instantiateMinerals(): Seq[MineralCrystalImpl] =
17 | for ((MineralSpawn(size, position), id) <- minerals.zipWithIndex)
18 | yield new MineralCrystalImpl(size, id, position)
19 | }
20 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/game/MultiplayerConfig.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.game
2 |
3 | import cwinter.codecraft.core.api.Player
4 | import cwinter.codecraft.core.multiplayer.{RemoteClient, RemoteServer}
5 |
6 | import scala.concurrent.duration._
7 | import scala.concurrent.duration.Duration
8 |
9 | sealed trait MultiplayerConfig {
10 | def isMultiplayerGame: Boolean
11 | def commandRecorder: CommandRecorder
12 | def isLocalPlayer(player: Player): Boolean
13 | def timeoutSecs: Duration = 30.seconds
14 | }
15 |
16 | private[core] object SingleplayerConfig extends MultiplayerConfig {
17 | def isMultiplayerGame = false
18 | def commandRecorder = throw new Exception("Trying to call commandRecorder on SingleplayerConfig.")
19 | def isLocalPlayer(player: Player): Boolean = true
20 | }
21 |
22 | private[core] case class MultiplayerClientConfig(
23 | localPlayers: Set[Player],
24 | remotePlayers: Set[Player],
25 | server: RemoteServer
26 | ) extends MultiplayerConfig {
27 | def isMultiplayerGame = true
28 | def isLocalPlayer(player: Player): Boolean = localPlayers.contains(player)
29 | val commandRecorder = new CommandRecorder
30 | override def timeoutSecs: Duration = 24.hours
31 | }
32 |
33 | private[core] case class AuthoritativeServerConfig(
34 | localPlayers: Set[Player],
35 | remotePlayers: Set[Player],
36 | clients: Set[RemoteClient],
37 | updateCompleted: DroneWorldSimulator => Unit,
38 | onTimeout: DroneWorldSimulator => Unit,
39 | playerTimeoutSecs: Duration
40 | ) extends MultiplayerConfig {
41 | def isMultiplayerGame = true
42 | def isLocalPlayer(player: Player): Boolean = localPlayers.contains(player)
43 | override def timeoutSecs: Duration = playerTimeoutSecs
44 | val commandRecorder = new CommandRecorder
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/game/SimulationContext.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.game
2 |
3 | import cwinter.codecraft.core.objects.MineralCrystalImpl
4 | import cwinter.codecraft.core.objects.drone.DroneImpl
5 |
6 | private[codecraft] case class SimulationContext(
7 | droneRegistry: Map[Int, DroneImpl],
8 | mineralRegistry: Map[Int, MineralCrystalImpl],
9 | timestep: Int
10 | ) {
11 | def drone(id: Int): DroneImpl = droneRegistry(id)
12 | def maybeDrone(id: Int): Option[DroneImpl] = {
13 | if (droneRegistry.contains(id)) {
14 | Some(droneRegistry(id))
15 | } else {
16 | println(s"WARNING: possible desync, drone with id $id not found")
17 | None
18 | }
19 | }
20 | def mineral(id: Int): MineralCrystalImpl = mineralRegistry(id)
21 | }
22 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/game/SpecialRules.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.game
2 |
3 | import cwinter.codecraft.core.api.GameConstants.ModuleResourceCost
4 | import cwinter.codecraft.core.api.DroneSpec
5 | import cwinter.codecraft.util.maths.RNG
6 |
7 | case class SpecialRules(
8 | // Increases damage taken by mothership by this factor.
9 | // If not a whole integer, fractional damage is applied probabilistically.
10 | mothershipDamageMultiplier: Double = 1.0,
11 | costModifierSize: Array[Double] = Array(1.0, 1.0, 1.0, 1.0),
12 | costModifierMissiles: Double = 1.0,
13 | costModifierShields: Double = 1.0,
14 | costModifierStorage: Double = 1.0,
15 | costModifierConstructor: Double = 1.0,
16 | costModifierEngines: Double = 1.0
17 | ) {
18 | private[codecraft] def modifiedCost(rng: RNG, spec: DroneSpec): Int = {
19 | val sizeModifier = if (spec.moduleCount - 1 < costModifierSize.length) {
20 | costModifierSize(spec.moduleCount - 1)
21 | } else {
22 | 1.0
23 | }
24 | def discretize(double: Double): Int = {
25 | val fractional = if (rng.bernoulli(double - double.floor)) 1 else 0
26 | double.floor.toInt + fractional
27 | }
28 |
29 | discretize(
30 | (spec.missileBatteries * costModifierMissiles +
31 | spec.shieldGenerators * costModifierShields +
32 | spec.storageModules * costModifierStorage +
33 | spec.constructors * costModifierConstructor +
34 | spec.engines * costModifierEngines) * ModuleResourceCost * sizeModifier)
35 | }
36 |
37 | }
38 |
39 | object SpecialRules {
40 | def default: SpecialRules = SpecialRules()
41 | }
42 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/game/WinCondition.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.game
2 |
3 | sealed trait WinCondition
4 | case object DestroyEnemyMotherships extends WinCondition
5 | case class LargestFleet(timeout: Int) extends WinCondition
6 | case object DestroyAllEnemies extends WinCondition
7 | case class DroneCount(count: Int) extends WinCondition
8 |
9 | object WinCondition {
10 | def default: Seq[WinCondition] = Seq(DestroyEnemyMotherships, LargestFleet(15 * 60 * 60))
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/graphics/BasicHomingMissileModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.graphics
2 |
3 | import cwinter.codecraft.graphics.engine.WorldObjectDescriptor
4 | import cwinter.codecraft.graphics.model.{SimpleModelBuilder, Model, ModelBuilder}
5 | import cwinter.codecraft.graphics.primitives.Polygon
6 | import cwinter.codecraft.util.maths.{ColorRGB, ColorRGBA, VertexXY}
7 |
8 |
9 | private[codecraft] case class BasicHomingMissileModel(
10 | x: Float,
11 | y: Float,
12 | playerColor: ColorRGB
13 | ) extends SimpleModelBuilder[BasicHomingMissileModel, Unit] with WorldObjectDescriptor[Unit] {
14 |
15 | def model =
16 | Polygon(
17 | material = rs.TranslucentAdditive,
18 | n = 10,
19 | colorMidpoint = ColorRGBA(1, 1, 1, 1),
20 | colorOutside = ColorRGBA(playerColor, 1),
21 | radius = 5,
22 | position = VertexXY(x, y),
23 | zPos = 3
24 | ).noCaching
25 |
26 |
27 | override def signature = this
28 | override def isCacheable = false
29 | override def allowCaching = false
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/graphics/CollisionMarkerModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.graphics
2 |
3 | import cwinter.codecraft.graphics.engine.WorldObjectDescriptor
4 | import cwinter.codecraft.graphics.materials.Intensity
5 | import cwinter.codecraft.graphics.model._
6 | import cwinter.codecraft.graphics.primitives.PartialPolygonRing
7 | import cwinter.codecraft.util.maths.{ColorRGBA, NullVectorXY}
8 |
9 |
10 | private[codecraft] case class CollisionMarkerModel(
11 | radius: Float,
12 | orientation: Float
13 | ) extends CompositeModelBuilder[CollisionMarkerModel, Float] with WorldObjectDescriptor[Float] {
14 | val colorGradient = IndexedSeq.tabulate(15)(i => 1 - math.abs(i.toFloat - 7f) / 7f)
15 |
16 | override protected def buildSubcomponents: (Seq[ModelBuilder[_, Unit]], Seq[ModelBuilder[_, Float]]) = {
17 | val marker =
18 | PartialPolygonRing(
19 | position = NullVectorXY,
20 | orientation = orientation,
21 | zPos = 2,
22 | material = rs.TranslucentAdditivePIntensity,
23 | n = 14,
24 | colorInside = Seq.tabulate(15)(i => ColorRGBA(DefaultDroneColors.ColorThrusters, colorGradient(i) * 0f)),
25 | colorOutside = Seq.tabulate(15)(i => ColorRGBA(DefaultDroneColors.White, colorGradient(i) * 0.7f)),
26 | outerRadius = radius,
27 | innerRadius = radius - 10,
28 | fraction = 0.2f
29 | ).wireParameters[Float](Intensity)
30 |
31 | (Seq.empty, Seq(marker))
32 | }
33 |
34 | override def signature = this
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/graphics/DroneConstructorModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.graphics
2 |
3 | import cwinter.codecraft.graphics.engine.RenderStack
4 | import cwinter.codecraft.graphics.model._
5 | import cwinter.codecraft.graphics.primitives.Polygon
6 | import cwinter.codecraft.util.maths.{ColorRGB, ColorRGBA, VertexXY}
7 |
8 |
9 | private[graphics] case class DroneConstructorModel(
10 | colors: DroneColors,
11 | playerColor: ColorRGB,
12 | position: VertexXY
13 | )(implicit rs: RenderStack) extends CompositeModelBuilder[DroneConstructorModel, Unit] {
14 | override def signature: DroneConstructorModel = this
15 |
16 | override protected def buildSubcomponents: (Seq[ModelBuilder[_, Unit]], Seq[ModelBuilder[_, Unit]]) = {
17 | val module = Polygon(
18 | rs.GaussianGlow,
19 | 20,
20 | ColorRGBA(0.5f * playerColor + 0.5f * colors.White, 1),
21 | ColorRGBA(colors.White, 0),
22 | radius = 8,
23 | position = position,
24 | zPos = 1,
25 | orientation = 0,
26 | colorEdges = true
27 | )
28 |
29 | (Seq(module), Seq.empty)
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/graphics/DroneEnginesModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.graphics
2 |
3 | import cwinter.codecraft.graphics.engine.RenderStack
4 | import cwinter.codecraft.graphics.model.{CompositeModelBuilder, ModelBuilder}
5 | import cwinter.codecraft.graphics.primitives.Polygon
6 | import cwinter.codecraft.util.maths.{ColorRGB, Geometry, VertexXY}
7 |
8 |
9 | private[graphics] case class DroneEnginesModel(
10 | position: VertexXY,
11 | colors: DroneColors,
12 | playerColor: ColorRGB,
13 | t: Int
14 | )(implicit rs: RenderStack)
15 | extends CompositeModelBuilder[DroneEnginesModel, Unit] {
16 |
17 | def signature: DroneEnginesModel = this
18 |
19 |
20 | override protected def buildSubcomponents: (Seq[ModelBuilder[_, Unit]], Seq[ModelBuilder[_, Unit]]) = {
21 | val enginePositions = Geometry.polygonVertices2(3, radius = 5, orientation = 2 * math.Pi.toFloat * t / 100)
22 | val engines =
23 | for ((offset, i) <- enginePositions.zipWithIndex)
24 | yield Polygon(
25 | rs.MaterialXYZRGB,
26 | 5,
27 | playerColor,
28 | colors.ColorHull,
29 | radius = 4,
30 | position = position + offset,
31 | orientation = -2 * math.Pi.toFloat * t / 125,
32 | zPos = 1
33 | )
34 |
35 | (engines, Seq.empty)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/graphics/DroneShieldGeneratorModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.graphics
2 |
3 | import cwinter.codecraft.graphics.engine.RenderStack
4 | import cwinter.codecraft.graphics.model._
5 | import cwinter.codecraft.graphics.primitives.{Polygon, PolygonRing}
6 | import cwinter.codecraft.util.maths.Geometry._
7 | import cwinter.codecraft.util.maths.{ColorRGB, VertexXY}
8 |
9 |
10 | private[graphics] case class DroneShieldGeneratorModel(
11 | position: VertexXY,
12 | colors: DroneColors,
13 | playerColor: ColorRGB
14 | )(implicit rs: RenderStack)
15 | extends CompositeModelBuilder[DroneShieldGeneratorModel, Unit] {
16 | def signature = this
17 |
18 |
19 | override protected def buildSubcomponents: (Seq[ModelBuilder[_, Unit]], Seq[ModelBuilder[_, Unit]]) = {
20 | val radius = 3
21 | val gridposRadius = 2 * inradius(radius, 6)
22 | val gridpoints = VertexXY(0, 0) +: polygonVertices(6, radius = gridposRadius)
23 | val hexgrid =
24 | for (pos <- gridpoints)
25 | yield
26 | PolygonRing(
27 | material = rs.MaterialXYZRGB,
28 | n = 6,
29 | colorInside = colors.White,
30 | colorOutside = colors.White,
31 | innerRadius = radius - 0.5f,
32 | outerRadius = radius,
33 | position = pos + position,
34 | zPos = 1
35 | )
36 |
37 | val filling =
38 | for (pos <- gridpoints)
39 | yield
40 | Polygon(
41 | material = rs.MaterialXYZRGB,
42 | n = 6,
43 | colorMidpoint = playerColor,
44 | colorOutside = playerColor,
45 | radius = radius - 0.5f,
46 | position = pos + position,
47 | zPos = 1
48 | )
49 |
50 | (hexgrid ++ filling, Seq.empty)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/graphics/EnergyGlobeModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.graphics
2 |
3 | import cwinter.codecraft.graphics.engine.WorldObjectDescriptor
4 | import cwinter.codecraft.graphics.model.SimpleModelBuilder
5 | import cwinter.codecraft.graphics.primitives.Polygon
6 | import cwinter.codecraft.util.maths.{ColorRGB, ColorRGBA, NullVectorXY, Rectangle}
7 |
8 | private[codecraft] case class EnergyGlobeModel(fade: Float)
9 | extends SimpleModelBuilder[EnergyGlobeModel, Unit]
10 | with WorldObjectDescriptor[Unit] {
11 | require(fade >= 0)
12 | require(fade <= 1)
13 |
14 | override protected def model = {
15 | if (signature.fade == 1) {
16 | Polygon(
17 | material = rs.BloomShader,
18 | n = 7,
19 | colorMidpoint = ColorRGB(1, 1, 1),
20 | colorOutside = ColorRGB(0, 1, 0),
21 | radius = 2,
22 | position = NullVectorXY,
23 | zPos = 3
24 | )
25 | } else {
26 | Polygon(
27 | material = rs.TranslucentProportional,
28 | n = 7,
29 | colorMidpoint = ColorRGBA(1, 1, 1, signature.fade),
30 | colorOutside = ColorRGBA(0, 1, 0, signature.fade),
31 | radius = 2,
32 | position = NullVectorXY,
33 | zPos = 3
34 | )
35 | }
36 | }
37 |
38 | override def intersects(xPos: Float, yPos: Float, rectangle: Rectangle): Boolean =
39 | intersects(xPos, yPos, 20, rectangle) // FIXME
40 | override def signature = this
41 | }
42 |
43 | private[codecraft] object PlainEnergyGlobeModel extends EnergyGlobeModel(1)
44 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/graphics/EnergyGlobeModelFactory.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.graphics
2 |
3 | import cwinter.codecraft.graphics.engine.RenderStack
4 | import cwinter.codecraft.graphics.primitives.Polygon
5 | import cwinter.codecraft.util.maths.{ColorRGB, ColorRGBA, VertexXY}
6 |
7 |
8 | private[graphics] object EnergyGlobeModelFactory {
9 | def build(position: VertexXY, fade: Float = 1)(implicit rs: RenderStack) = {
10 | if (fade == 1) {
11 | Polygon(
12 | material = rs.BloomShader,
13 | n = 7,
14 | colorMidpoint = ColorRGB(1, 1, 1),
15 | colorOutside = ColorRGB(0, 1, 0),
16 | radius = 2,
17 | position = position,
18 | zPos = 3
19 | )
20 | } else {
21 | Polygon(
22 | material = rs.TranslucentProportional,
23 | n = 7,
24 | colorMidpoint = ColorRGBA(1, 1, 1, fade),
25 | colorOutside = ColorRGBA(0, 1, 0, fade),
26 | radius = 2,
27 | position = position,
28 | zPos = 3
29 | )
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/graphics/HomingMissileModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.graphics
2 |
3 | import cwinter.codecraft.graphics.engine.WorldObjectDescriptor
4 | import cwinter.codecraft.graphics.model._
5 | import cwinter.codecraft.graphics.primitives.QuadStrip
6 | import cwinter.codecraft.util.maths.{ColorRGB, ColorRGBA, VertexXY}
7 |
8 |
9 | private[codecraft] case class HomingMissileModel(
10 | positions: Seq[(Float, Float)],
11 | nMaxPos: Int,
12 | playerColor: ColorRGB
13 | ) extends SimpleModelBuilder[HomingMissileModel, Unit] with WorldObjectDescriptor[Unit] {
14 | override protected def model = {
15 | if (positions.length < 2) EmptyModelBuilder
16 | else {
17 | val midpoints = positions.map { case (x, y) => VertexXY(x, y)}
18 | val n = nMaxPos
19 | val colorHead = ColorRGB(1, 1, 1)
20 | val colorTail = playerColor
21 | val colors = positions.zipWithIndex.map {
22 | case (_, index) =>
23 | val x = index / (n - 1).toFloat
24 | val z = x * x
25 | ColorRGBA(z * colorHead + (1 - z) * colorTail, x)
26 | }
27 |
28 |
29 |
30 | QuadStrip(
31 | rs.TranslucentAdditive,
32 | midpoints,
33 | colors,
34 | 3,
35 | zPos = 3
36 | ).noCaching
37 | }
38 | }
39 |
40 | override def signature = this
41 | override def isCacheable = false
42 | override def allowCaching = false
43 | }
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/graphics/LightFlashModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.graphics
2 |
3 | import cwinter.codecraft.graphics.engine.{GraphicsContext, WorldObjectDescriptor}
4 | import cwinter.codecraft.graphics.materials.Intensity
5 | import cwinter.codecraft.graphics.model.{CompositeModel, Model, ModelBuilder}
6 | import cwinter.codecraft.graphics.primitives.Polygon
7 | import cwinter.codecraft.util.maths.ColorRGBA
8 |
9 |
10 | private[codecraft] case object LightFlashModel
11 | extends ModelBuilder[Any, Float] with WorldObjectDescriptor[Float] {
12 |
13 | override protected def buildModel(context: GraphicsContext): Model[Float] = {
14 | val flash = Polygon(
15 | rs.GaussianGlowPIntensity,
16 | 25,
17 | ColorRGBA(1, 1, 1, 1),
18 | ColorRGBA(1, 1, 1, 0),
19 | radius = 1,
20 | zPos = -1
21 | ).getModel(context).scalable(context.useTransposedModelview)
22 |
23 | val flashConnected =
24 | flash.wireParameters[Float]{
25 | stage =>
26 | val intensity = Intensity(1 - stage)
27 | val radius = 60 * stage + 5
28 | (intensity, radius)
29 | }
30 |
31 | CompositeModel(
32 | Seq.empty,
33 | Seq(flashConnected)
34 | )
35 | }
36 |
37 | override def signature = this
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/graphics/MineralCrystalModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.graphics
2 |
3 | import cwinter.codecraft.graphics.engine.WorldObjectDescriptor
4 | import cwinter.codecraft.graphics.model.{SimpleModelBuilder, Model, ModelBuilder}
5 | import cwinter.codecraft.graphics.primitives.Polygon
6 | import cwinter.codecraft.util.maths.{ColorRGB, Rectangle, VertexXY}
7 |
8 |
9 | private[codecraft] case class MineralCrystalModel(
10 | size: Int,
11 | xPos: Float,
12 | yPos: Float,
13 | orientation: Float
14 | ) extends SimpleModelBuilder[MineralCrystalModel, Unit] with WorldObjectDescriptor[Unit] {
15 |
16 | override protected def model = {
17 | val size = signature.size
18 | val radius = math.sqrt(size).toFloat * 3
19 |
20 | Polygon(
21 | rs.BloomShader,
22 | n = 5,
23 | colorMidpoint = ColorRGB(0.03f, 0.6f, 0.03f),
24 | colorOutside = ColorRGB(0.0f, 0.1f, 0.0f),
25 | radius = radius,
26 | zPos = -5,
27 | position = VertexXY(xPos, yPos),
28 | orientation = orientation
29 | )
30 | }
31 |
32 | override def intersects(xPos: Float, yPos: Float, rectangle: Rectangle): Boolean =
33 | intersects(this.xPos, this.yPos, 50, rectangle)
34 | override def signature = this
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/multiplayer/RemoteClient.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.multiplayer
2 |
3 | import cwinter.codecraft.core.api.Player
4 | import cwinter.codecraft.core.game.SimulationContext
5 | import cwinter.codecraft.core.objects.drone._
6 |
7 | import scala.concurrent.Future
8 |
9 |
10 | private[core] trait RemoteClient {
11 | def waitForCommands()(implicit context: SimulationContext): Future[Seq[(Int, DroneCommand)]]
12 | def sendCommands(commands: Seq[(Int, DroneCommand)]): Unit
13 | def sendWorldState(worldStateMessage: WorldStateMessage): Unit
14 | def players: Set[Player]
15 | def close(reason: GameClosed.Reason): Unit
16 | def msSinceLastResponse: Int
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/multiplayer/RemoteServer.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.multiplayer
2 |
3 | import cwinter.codecraft.core.game.SimulationContext
4 | import cwinter.codecraft.core.objects.drone._
5 |
6 | import scala.concurrent.Future
7 |
8 | private[core] trait RemoteServer {
9 | type Result[T] = Future[Either[T, GameClosed.Reason]]
10 | def receiveCommands()(implicit context: SimulationContext): Result[Seq[(Int, DroneCommand)]]
11 | def receiveWorldState(): Result[WorldStateMessage]
12 | def sendCommands(commands: Seq[(Int, DroneCommand)]): Unit
13 | def gameClosed: Option[GameClosed.Reason]
14 | def msSinceLastResponse: Int
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/multiplayer/ServerStatus.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.multiplayer
2 |
3 |
4 | case class Status(
5 | clientWaiting: Boolean,
6 | runningGames: Int,
7 | connections: Int,
8 | maxConnections: Int
9 | )
10 |
11 | case class DetailedStatus(
12 | clientWaiting: Boolean,
13 | connections: Int,
14 | games: Seq[GameStatus],
15 | timestamp: Long,
16 | startTimestamp: Long
17 | )
18 |
19 | case class GameStatus(
20 | closeReason: Option[String],
21 | fps: Int,
22 | averageFPS: Int,
23 | timestep: Long,
24 | startTimestamp: Long,
25 | endTimestamp: Option[Long],
26 | msSinceLastResponse: Int,
27 | currentPhase: String,
28 | bandwidthUp: Double,
29 | bandwidthDown: Double
30 | )
31 |
32 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/multiplayer/WebsocketClient.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.multiplayer
2 |
3 | import java.nio.ByteBuffer
4 |
5 | private[core] trait WebsocketClient {
6 | protected var onOpenCallbacks = Vector.empty[WebsocketClient => Unit]
7 | protected var onMessageCallbacks = Vector.empty[(WebsocketClient, ByteBuffer) => Unit]
8 | protected var onCloseCallbacks = Vector.empty[WebsocketClient => Unit]
9 |
10 | def connect(): Unit
11 |
12 | def sendMessage(message: ByteBuffer): Unit
13 |
14 | def registerOnOpen(callback: WebsocketClient => Unit): Unit = synchronized {
15 | onOpenCallbacks :+= callback
16 | }
17 |
18 | def registerOnMessage(callback: (WebsocketClient, ByteBuffer) => Unit): Unit = synchronized {
19 | onMessageCallbacks :+= callback
20 | }
21 |
22 | def registerOnClose(callback: WebsocketClient => Unit): Unit = synchronized {
23 | onCloseCallbacks :+= callback
24 | }
25 |
26 | protected def runOnMessageCallbacks(msg: ByteBuffer): Unit = onMessageCallbacks.foreach(_(this, msg))
27 |
28 | protected def runOnOpenCallbacks(): Unit = onOpenCallbacks.foreach(_(this))
29 |
30 | protected def runOnCloseCallbacks(): Unit = onCloseCallbacks.foreach(_(this))
31 | }
32 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/EnergyGlobeObject.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects
2 |
3 | import cwinter.codecraft.core.game.{RemoveEnergyGlobeAnimation, SimulatorEvent}
4 | import cwinter.codecraft.core.graphics.{EnergyGlobeModel, PlainEnergyGlobeModel}
5 | import cwinter.codecraft.core.objects.drone.DroneImpl
6 | import cwinter.codecraft.graphics.engine.{ModelDescriptor, PositionDescriptor}
7 | import cwinter.codecraft.util.maths.Vector2
8 |
9 |
10 | private[core] class EnergyGlobeObject(
11 | val frameOfReference: DroneImpl,
12 | var position: Vector2,
13 | var tta: Int,
14 | targetPosition: Vector2
15 | ) extends WorldObject {
16 | final val FadeTime = 15
17 | val velocity = (targetPosition - position) / tta
18 | var fade = FadeTime
19 | val id = -1
20 |
21 | override private[core] def descriptor: Seq[ModelDescriptor[_]] = {
22 | val dronePos = frameOfReference.position
23 | val sin = math.sin(frameOfReference.dynamics.orientation)
24 | val cos = math.cos(frameOfReference.dynamics.orientation)
25 | val x = cos * position.x - sin * position.y
26 | val y = sin * position.x + cos * position.y
27 | Seq(
28 | ModelDescriptor(
29 | PositionDescriptor(
30 | (x + dronePos.x).toFloat,
31 | (y + dronePos.y).toFloat, 0),
32 | if (tta > 0) PlainEnergyGlobeModel
33 | else EnergyGlobeModel(fade / FadeTime.toFloat)
34 | )
35 | )
36 | }
37 |
38 | override def update(): Seq[SimulatorEvent] = {
39 | if (tta > 0) {
40 | tta -= 1
41 | position += velocity
42 | } else {
43 | fade -= 1
44 | }
45 | if (fade == 0) Seq(RemoveEnergyGlobeAnimation(this))
46 | else Seq()
47 | }
48 |
49 | override private[core] def isDead: Boolean = fade <= 0
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/IDGenerator.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects
2 |
3 |
4 | private[codecraft] class IDGenerator(group: Int) {
5 | import IDGenerator._
6 | private[this] var count: Int = -1
7 | assert(group <= MaxFactions)
8 |
9 | def getAndIncrement(): Int = {
10 | count += 1
11 | count * MaxFactions + group
12 | }
13 | }
14 |
15 | private[codecraft] object IDGenerator {
16 | final val MaxFactions = 4
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/LightFlash.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects
2 |
3 | import cwinter.codecraft.core.game.{LightFlashDestroyed, SimulatorEvent}
4 | import cwinter.codecraft.core.graphics.LightFlashModel
5 | import cwinter.codecraft.graphics.engine.{ModelDescriptor, PositionDescriptor}
6 | import cwinter.codecraft.util.maths.Vector2
7 |
8 |
9 | private[core] class LightFlash(val position: Vector2) extends WorldObject {
10 | var stage: Float = 0
11 | val id = -1
12 | private val positionDescriptor =
13 | PositionDescriptor(position.x, position.y)
14 |
15 | override private[core] def descriptor: Seq[ModelDescriptor[_]] = Seq(
16 | ModelDescriptor(
17 | positionDescriptor,
18 | LightFlashModel,
19 | stage
20 | )
21 | )
22 |
23 | def update(): Seq[SimulatorEvent] = {
24 | stage += 1.0f / 10
25 |
26 | if (stage > 1) {
27 | Seq(LightFlashDestroyed(this))
28 | } else Seq.empty[SimulatorEvent]
29 | }
30 |
31 | def isDead: Boolean = stage > 1
32 | }
33 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/MissileDynamics.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects
2 |
3 | import cwinter.codecraft.core.objects.drone.{ComputedDroneDynamics, DroneDynamics}
4 | import cwinter.codecraft.util.maths.{Rectangle, Vector2}
5 |
6 | private[core] class MissileDynamics(
7 | val speed: Double,
8 | val target: DroneDynamics,
9 | val ownerID: Int,
10 | val missile: HomingMissile,
11 | initialPosition: Vector2,
12 | initialTime: Double
13 | ) extends ConstantVelocityDynamics(1, ownerID, false, initialPosition, initialTime) {
14 | var hasHit = false
15 |
16 | override def handleObjectCollision(other: ConstantVelocityDynamics): Unit = {
17 | this.remove()
18 |
19 | hasHit = true
20 | other match {
21 | case otherMissile: MissileDynamics => otherMissile.remove()
22 | case otherDrone: ComputedDroneDynamics => otherDrone.drone.missileHit(missile)
23 | }
24 | }
25 |
26 | override def handleWallCollision(areaBounds: Rectangle): Unit = {
27 | this.remove()
28 | // just die on collision (or maybe bounce?)
29 | }
30 |
31 | def recomputeVelocity(): Unit = {
32 | val targetDirection = target.pos - pos
33 | if (!target.removed && targetDirection.length >= 0.0001) {
34 | velocity = speed * targetDirection.normalized
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/WorldObject.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects
2 |
3 | import cwinter.codecraft.collisions.Positionable
4 | import cwinter.codecraft.core.game.SimulatorEvent
5 | import cwinter.codecraft.graphics.engine.ModelDescriptor
6 | import cwinter.codecraft.util.maths.Vector2
7 |
8 |
9 | private[core] trait WorldObject {
10 | def position: Vector2
11 |
12 | def update(): Seq[SimulatorEvent]
13 | private[core] def descriptor: Seq[ModelDescriptor[_]]
14 | private[core] val id: Int
15 | private[core] def isDead: Boolean
16 | }
17 |
18 |
19 | private[core] object WorldObject {
20 | implicit object WorldObjectIsPositionable extends Positionable[WorldObject] {
21 | override def position(t: WorldObject): Vector2 = t.position
22 | }
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/drone/DroneContext.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.api.Player
4 | import cwinter.codecraft.core.errors.Errors
5 | import cwinter.codecraft.core.game.{CommandRecorder, DroneWorldSimulator, GameConfig, SpecialRules}
6 | import cwinter.codecraft.core.objects.IDGenerator
7 | import cwinter.codecraft.core.replay.{NullReplayRecorder, ReplayRecorder}
8 | import cwinter.codecraft.graphics.engine.Debug
9 | import cwinter.codecraft.util.maths.{RNG, Rectangle}
10 |
11 | private[core] case class DroneContext(
12 | player: Player,
13 | worldSize: Rectangle,
14 | tickPeriod: Int,
15 | commandRecorder: Option[CommandRecorder],
16 | debugLog: Option[DroneDebugLog],
17 | idGenerator: IDGenerator,
18 | rng: RNG,
19 | isLocallyComputed: Boolean,
20 | isMultiplayer: Boolean,
21 | simulator: DroneWorldSimulator,
22 | replayRecorder: ReplayRecorder = NullReplayRecorder,
23 | debug: Debug,
24 | errors: Errors,
25 | specialRules: SpecialRules
26 | ) {
27 | def settings = simulator.settings
28 | def isAuthoritativeServer: Boolean = isMultiplayer && isLocallyComputed
29 | def isMultiplayerClient: Boolean = isMultiplayer && !isLocallyComputed
30 |
31 | var missileHits = List.empty[MissileHit]
32 | var mineralHarvests = List.empty[MineralHarvest]
33 | }
34 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/drone/DroneDebugLog.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.util.maths.Vector2
4 |
5 |
6 | private[codecraft] class DroneDebugLog {
7 | private[this] var events = List.empty[Record]
8 |
9 |
10 | def record(time: Int, droneID: Int, datum: DebugLogDatum): Unit =
11 | events ::= Record(time, droneID, datum)
12 |
13 | def retrieve(begin: Int, end: Int, droneID: Int): Seq[(Int, DebugLogDatum)] = {
14 | for {
15 | Record(t, id, datum) <- events
16 | if begin <= t && t <= end && droneID == id
17 | } yield (t, datum)
18 | }.reverse
19 |
20 | def findDrone(time: Int, location: Vector2): Option[Int] = {
21 | events.find {
22 | case Record(t, _, Position(pos, _)) => pos == location
23 | case _ => false
24 | }
25 | }.map(_.droneID)
26 |
27 | private case class Record(time: Int, droneID: Int, event: DebugLogDatum)
28 | }
29 |
30 |
31 | private[codecraft] sealed trait DebugLogDatum
32 | private[codecraft] case class Position(pos: Vector2, orientation: Float) extends DebugLogDatum
33 | private[codecraft] case class Command(droneCommand: DroneCommand, redundant: Boolean) extends DebugLogDatum
34 | private[codecraft] case class Collision(position: Vector2, otherDroneID: Int) extends DebugLogDatum
35 | private[codecraft] case class DamageTaken(damage: Int, finalHealth: Int) extends DebugLogDatum
36 | private[codecraft] case class UnstructuredEvent(message: String) extends DebugLogDatum
37 |
38 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/drone/DroneDynamicsBasics.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.game.SimulationContext
4 | import cwinter.codecraft.util.maths.Vector2
5 |
6 | private[core] trait DroneDynamics {
7 | def setMovementCommand(command: MovementCommand): Boolean
8 | def checkArrivalConditions(): Option[DroneEvent]
9 | def pos: Vector2
10 | def setTime(time: Double)
11 | def recomputeVelocity(): Unit
12 | def remove(): Unit
13 | def removed: Boolean
14 | def orientation: Float
15 | def isMoving: Boolean
16 | def activeCommand: MovementCommand
17 | def isStunned: Boolean
18 | }
19 |
20 | private[core] trait SyncableDroneDynamics {
21 | def synchronize(state: DroneMovementMsg)(implicit context: SimulationContext): Unit
22 | }
23 |
24 | private[core] trait DroneDynamicsBasics extends DroneDynamics {
25 | protected var _movementCommand: MovementCommand = HoldPosition
26 | protected var _orientation: Float = 0
27 |
28 | def setMovementCommand(command: MovementCommand): Boolean = {
29 | command match {
30 | case MoveToPosition(p) if p ~ pos => return true
31 | case _ =>
32 | }
33 | val redundant = command == _movementCommand
34 | _movementCommand = command
35 | redundant
36 | }
37 |
38 | def checkArrivalConditions(): Option[DroneEvent] = {
39 | val event = arrivalEvent
40 | if (event.nonEmpty) halt()
41 | event
42 | }
43 |
44 | private[drone] def orientation_=(value: Float) = _orientation = value
45 | def orientation: Float = _orientation
46 | def isMoving: Boolean = _movementCommand != HoldPosition
47 | def activeCommand: MovementCommand = _movementCommand
48 |
49 | protected def arrivalEvent: Option[DroneEvent]
50 | protected def halt(): Unit
51 | }
52 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/drone/DroneEventQueue.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 |
4 | private[core] trait DroneEventQueue { self: DroneImpl =>
5 | private[this] val eventQueue = collection.mutable.Queue[DroneEvent](Spawned)
6 | private[this] var t = 0
7 |
8 | def processEvents(): Unit = {
9 | resetMessageDisplay()
10 | controller.willProcessEvents()
11 |
12 | t += 1
13 | if (isDead) controller.onDeath()
14 | else {
15 | eventQueue foreach {
16 | case Destroyed => // this should never be executed
17 | case MineralEntersSightRadius(mineral) =>
18 | controller.onMineralEntersVision(mineral.getHandle(player))
19 | case ArrivedAtPosition => controller.onArrivesAtPosition()
20 | case ArrivedAtDrone(drone) => controller.onArrivesAtDrone(drone.wrapperFor(player))
21 | case ArrivedAtMineral(mineral) => controller.onArrivesAtMineral(mineral.getHandle(player))
22 | case DroneEntersSightRadius(drone) => controller.onDroneEntersVision(drone.wrapperFor(player))
23 | case Spawned => // handled by simulator to ensure onSpawn is called before any other events
24 | case event => throw new Exception(s"Unhandled event! $event")
25 | }
26 | eventQueue.clear()
27 | controller.onTick()
28 | }
29 | }
30 |
31 | private[core] def enqueueEvent(event: DroneEvent): Unit = eventQueue.enqueue(event)
32 | }
33 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/drone/DroneModules.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.game.{SpawnEnergyGlobeAnimation, SimulatorEvent}
4 | import cwinter.codecraft.core.objects.EnergyGlobeObject
5 |
6 |
7 | private[core] trait DroneModules { self: DroneImpl =>
8 | protected val weapons = spec.constructMissilesBatteries(this)
9 | protected[core] val storage = spec.constructStorage(this, startingResources)
10 | protected val manipulator = spec.constructManipulatorModules(this)
11 | protected val shieldGenerators = spec.constructShieldGenerators(this)
12 | protected val engines = spec.constructEngineModules(this)
13 | val droneModules = Seq(weapons, storage, manipulator, shieldGenerators, engines)
14 |
15 |
16 | def updateModules(): Seq[SimulatorEvent] = {
17 | var simulatorEvents = List.empty[SimulatorEvent]
18 | for (Some(m) <- droneModules) {
19 | val (events, resourceDepletions, resourceSpawns) = m.update(storedResources)
20 | simulatorEvents :::= events.toList
21 | for {
22 | s <- storage
23 | rd <- resourceDepletions
24 | pos = s.withdrawEnergyGlobe()
25 | if context.settings.allowEnergyGlobeAnimation
26 | } simulatorEvents ::= SpawnEnergyGlobeAnimation(new EnergyGlobeObject(this, pos, 30, rd))
27 | for (s <- storage; rs <- resourceSpawns) s.depositEnergyGlobe(rs)
28 | }
29 | simulatorEvents
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/drone/DroneMovementDetector.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.util.maths.Vector2
4 |
5 |
6 | private[core] trait DroneMovementDetector { self: DroneImpl =>
7 | private[this] var _oldPosition = Vector2.Null
8 | private[this] var _oldOrientation = 0.0
9 | private[this] var _hasMoved: Boolean = true
10 |
11 |
12 | def recomputeHasMoved(): Unit = {
13 | _hasMoved = _oldPosition != position || _oldOrientation != dynamics.orientation
14 | _oldPosition = position
15 | _oldOrientation = dynamics.orientation
16 | }
17 |
18 | def hasMoved = _hasMoved
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/drone/EnemyDrone.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.api.{Player, ObjectNotVisibleException, Drone, DroneSpec}
4 | import cwinter.codecraft.core.errors.Errors
5 | import cwinter.codecraft.util.PrecomputedHashcode
6 | import cwinter.codecraft.util.maths.Vector2
7 |
8 | /**
9 | * Wrapper around drone class to allow users to query a subset of properties of enemy drones.
10 | */
11 | private[core] class EnemyDrone(
12 | private[core] val drone: DroneImpl,
13 | private val holder: Player // the player to whom the handle is given
14 | ) extends Drone {
15 | private[this] var _lastKnownPosition: Vector2 = drone.position
16 | private[this] var _lastKnownOrientation: Double = drone.dynamics.orientation
17 |
18 |
19 | override def lastKnownPosition: Vector2 = _lastKnownPosition
20 | override def lastKnownOrientation: Double = _lastKnownOrientation
21 | override def isEnemy: Boolean = true
22 | override def isVisible: Boolean =
23 | drone.player == holder || drone.dronesInSight.exists(_.drone.player == holder)
24 |
25 | private[core] def recordPosition(): Unit =
26 | if (isVisible) {
27 | _lastKnownPosition = position
28 | _lastKnownOrientation = drone.dynamics.orientation
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/drone/EnginesModule.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.game.SimulatorEvent
4 | import cwinter.codecraft.core.graphics.{EnginesDescriptor, DroneModuleDescriptor}
5 | import cwinter.codecraft.util.maths.Vector2
6 |
7 |
8 | private[core] class EnginesModule(positions: Seq[Int], owner: DroneImpl)
9 | extends DroneModule(positions, owner) {
10 |
11 | override def update(availableResources: Int): (Seq[SimulatorEvent], Seq[Vector2], Seq[Vector2]) = {
12 | if (owner.context.settings.allowModuleAnimation) owner.invalidateModelCache()
13 | NoEffects
14 | }
15 |
16 | override def descriptors: Seq[DroneModuleDescriptor] = positions.map(EnginesDescriptor)
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/drone/MissileBatteryModule.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.api.GameConstants.{MissileCooldown, MissileLockOnRange}
4 | import cwinter.codecraft.core.game.{SimulatorEvent, SpawnHomingMissile}
5 | import cwinter.codecraft.core.graphics.{DroneModuleDescriptor, MissileBatteryDescriptor}
6 | import cwinter.codecraft.util.maths.Vector2
7 |
8 |
9 | private[core] class MissileBatteryModule(positions: Seq[Int], owner: DroneImpl)
10 | extends DroneModule(positions, owner) {
11 |
12 | private[this] var nextEffect = NoEffects
13 | private[this] var _cooldown = 0
14 |
15 | def cooldown: Int = _cooldown
16 |
17 |
18 | override def update(availableResources: Int): (Seq[SimulatorEvent], Seq[Vector2], Seq[Vector2]) = {
19 | if (_cooldown > 0) _cooldown = _cooldown - 1
20 |
21 | val result = nextEffect
22 | nextEffect = NoEffects
23 | result
24 | }
25 |
26 |
27 | def fire(target: DroneImpl): Unit = {
28 | if ((target.position - owner.position).length > MissileLockOnRange) {
29 | owner.warn(s"Cannot fire homing missiles unless the target is within lock-on range ($MissileLockOnRange)")
30 | } else {
31 | if (_cooldown <= 0) {
32 | _cooldown = MissileCooldown
33 |
34 | val missiles =
35 | for (pos <- absoluteModulePositions)
36 | yield SpawnHomingMissile(owner.player, pos, owner.context.idGenerator.getAndIncrement(), target)
37 |
38 | nextEffect = (missiles, Seq.empty[Vector2], Seq.empty[Vector2])
39 | }
40 | }
41 | }
42 |
43 | override def descriptors: Seq[DroneModuleDescriptor] = positions.map(MissileBatteryDescriptor(_))
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/drone/RemoteDroneDynamics.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.game.SimulationContext
4 | import cwinter.codecraft.util.maths.Vector2
5 |
6 | private[core] class RemoteDroneDynamics(initialPos: Vector2)
7 | extends DroneDynamicsBasics
8 | with SyncableDroneDynamics {
9 | private[this] var _isStunned: Boolean = false
10 | private[this] var _removed: Boolean = false
11 | private[this] var _arrivalEvent: Option[DroneEvent] = None
12 | private[this] var position = initialPos
13 |
14 | override def setTime(time: Double): Unit = {}
15 | override def remove(): Unit = _removed = true
16 | override def removed: Boolean = _removed
17 |
18 | def synchronize(state: DroneMovementMsg)(implicit context: SimulationContext): Unit = state match {
19 | case PositionAndOrientationChanged(newPosition, newOrientation, _) =>
20 | position = newPosition
21 | _orientation = newOrientation
22 | case PositionChanged(newPosition, _) => position = newPosition
23 | case OrientationChanged(newOrientation, _) => _orientation = newOrientation
24 | case NewArrivalEvent(event, _) => _arrivalEvent = Some(DroneEvent(event))
25 | case Stunned(isStunned, _) => _isStunned = isStunned
26 | }
27 |
28 | override def recomputeVelocity(): Unit = _arrivalEvent = None
29 |
30 | override def arrivalEvent: Option[DroneEvent] = _arrivalEvent
31 | override def halt(): Unit = _movementCommand = HoldPosition
32 | override def pos: Vector2 = position
33 | override def isStunned: Boolean = _isStunned
34 | }
35 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/objects/drone/SpeculatingDroneDynamics.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.objects.drone
2 |
3 | import cwinter.codecraft.core.game.SimulationContext
4 | import cwinter.codecraft.util.maths.Vector2
5 |
6 | private[core] class SpeculatingDroneDynamics(
7 | val remote: RemoteDroneDynamics,
8 | val speculative: ComputedDroneDynamics
9 | ) extends DroneDynamics
10 | with SyncableDroneDynamics {
11 |
12 | override def setMovementCommand(command: MovementCommand): Boolean = {
13 | remote.setMovementCommand(command)
14 | speculative.setMovementCommand(command)
15 | }
16 |
17 | override def synchronize(state: DroneMovementMsg)(implicit context: SimulationContext): Unit = {
18 | remote.synchronize(state)
19 | }
20 |
21 | def syncSpeculator(): Boolean = {
22 | if (remote.orientation != speculative.orientation) speculative.orientation = remote.orientation
23 | if (remote.pos != speculative.pos) {
24 | speculative.setPosition(remote.pos)
25 | true
26 | } else false
27 | }
28 |
29 | override def activeCommand: MovementCommand = remote.activeCommand
30 | override def orientation: Float = speculative.orientation
31 | override def checkArrivalConditions(): Option[DroneEvent] = {
32 | speculative.checkArrivalConditions()
33 | remote.checkArrivalConditions()
34 | }
35 | override def isMoving: Boolean = remote.isMoving
36 | override def removed: Boolean = remote.removed
37 | override def remove(): Unit = {
38 | remote.remove()
39 | speculative.remove()
40 | }
41 | override def pos: Vector2 = speculative.pos
42 | override def setTime(time: Double): Unit = speculative.setTime(time)
43 | override def recomputeVelocity(): Unit = {
44 | remote.recomputeVelocity()
45 | speculative.recomputeVelocity()
46 | }
47 |
48 | override def isStunned: Boolean = speculative.isStunned
49 | }
50 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/replay/ConsoleReplayRecorder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.replay
2 |
3 | private[codecraft] class ConsoleReplayRecorder extends ReplayRecorder {
4 | val replay = new StringBuilder
5 |
6 | protected override def writeLine(string: String): Unit = {
7 | println(string)
8 | replay append (string + "\n")
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/replay/DummyDroneController.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.replay
2 |
3 | import cwinter.codecraft.core.api.{MineralCrystal, Drone, DroneController}
4 |
5 | class DummyDroneController extends DroneController {
6 | override def onSpawn(): Unit = ()
7 | override def onMineralEntersVision(mineralCrystal: MineralCrystal): Unit = ()
8 | override def onTick(): Unit = ()
9 | override def onArrivesAtPosition(): Unit = ()
10 | override def onDeath(): Unit = ()
11 | override def onDroneEntersVision(drone: Drone): Unit = ()
12 | }
13 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/replay/NullReplayRecorder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.replay
2 |
3 | import cwinter.codecraft.core.api.{DroneSpec, Player}
4 | import cwinter.codecraft.core.game.GameConfig
5 | import cwinter.codecraft.core.objects.drone.DroneCommand
6 | import cwinter.codecraft.util.maths.{Rectangle, Vector2}
7 |
8 | private[codecraft] object NullReplayRecorder extends ReplayRecorder {
9 | override def recordInitialWorldState(config: GameConfig): Unit = ()
10 | override def recordVersion(): Unit = ()
11 | override def recordSpawn(droneSpec: DroneSpec, position: Vector2, player: Player, resources: Int, name: Option[String]): Unit = ()
12 | override def recordWorldSize(rectangle: Rectangle): Unit = ()
13 | override def record(droneID: Int, droneCommand: DroneCommand): Unit = ()
14 | override def writeLine(string: String): Unit = ()
15 | }
16 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/replay/Replay.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.replay
2 |
3 |
4 | object Replay {
5 | final val CurrentVersion = "0.6.0"
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/core/shared/src/main/scala/cwinter/codecraft/core/replay/StringReplayRecorder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.core.replay
2 |
3 | private[core] class StringReplayRecorder extends ReplayRecorder {
4 | final val replay = new StringBuilder()
5 |
6 |
7 | protected override def writeLine(string: String): Unit = {
8 | replay.append(string + "\n")
9 | }
10 |
11 | override def replayString: Option[String] = Some(replay.toString)
12 | }
13 |
--------------------------------------------------------------------------------
/docs/root-doc.txt:
--------------------------------------------------------------------------------
1 | These pages contain a comprehensive documentation of the Scala/Java API for [[http://www.codecraftgame.org CodeCraft]].
2 | An overview of the game mechanics can be found [[http://www.codecraftgame.org/docs/mechanics here]].
3 | Almost all of this applies directly to the JavaScript version as well, a description of the differences can be found [[http://www.codecraftgame.org/docs/javascript here]].
4 |
5 | The main class of interest is [[cwinter.codecraft.core.api.DroneController DroneController]]
6 | (Scala/JavaScript) and [[cwinter.codecraft.core.api.JDroneController JDroneController]] (Java),
7 | as it contains all the methods to give orders to drones, query their state and receive event notifications.
8 | In Scala/Java you will also make use of [[cwinter.codecraft.core.api.TheGameMaster TheGameMaster]] to select a level and start the game.
9 |
10 |
--------------------------------------------------------------------------------
/graphics/js/src/main/resources/rgb1_brighten_fs.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | varying vec3 fragmentCol;
4 |
5 | void main(void) {
6 | gl_FragColor = vec4(1.3 * fragmentCol, 1);
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/graphics/js/src/main/resources/rgb1_fs.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | varying vec3 fragmentCol;
4 |
5 | void main(void) {
6 | gl_FragColor = vec4(fragmentCol, 1);
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/graphics/js/src/main/resources/rgba_fs.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | varying vec4 fragmentCol;
4 |
5 | void main(void) {
6 | gl_FragColor = fragmentCol;
7 | }
8 |
--------------------------------------------------------------------------------
/graphics/js/src/main/resources/rgba_gaussian_fs.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | varying vec4 fragmentCol;
4 | void main(void) {
5 | float x = 1.0 - fragmentCol.w;
6 | float alpha = exp(-5.0 * x * x);
7 | gl_FragColor = vec4(fragmentCol.x, fragmentCol.y, fragmentCol.z, alpha);
8 | }
9 |
--------------------------------------------------------------------------------
/graphics/js/src/main/resources/rgba_gaussian_pint_fs.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | varying vec4 fragmentCol;
4 |
5 | uniform float intensity;
6 |
7 | void main() {
8 | float x = 1.0 - fragmentCol.w;
9 | float alpha = exp(-5.0 * x * x);
10 | gl_FragColor = vec4(fragmentCol.x * intensity, fragmentCol.y * intensity, fragmentCol.z * intensity, alpha);
11 | }
12 |
--------------------------------------------------------------------------------
/graphics/js/src/main/resources/rgba_pint_fs.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | varying vec4 fragmentCol;
4 |
5 | uniform float intensity;
6 |
7 | void main() {
8 | gl_FragColor = intensity * fragmentCol;
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/graphics/js/src/main/resources/xyz_rgb_vs.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 vertexPos;
2 | attribute vec3 vertexCol;
3 |
4 | varying vec3 fragmentCol;
5 |
6 | uniform mat4 modelview;
7 | uniform mat4 projection;
8 |
9 | void main (void) {
10 | gl_Position = projection * modelview * vec4(vertexPos, 1.0);
11 |
12 | fragmentCol = vertexCol;
13 | }
14 |
--------------------------------------------------------------------------------
/graphics/js/src/main/resources/xyz_rgba_vs.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 vertexPos;
2 | attribute vec4 vertexCol;
3 |
4 | varying vec4 fragmentCol;
5 |
6 | uniform mat4 modelview;
7 | uniform mat4 projection;
8 |
9 | void main (void) {
10 | gl_Position = projection * modelview * vec4(vertexPos, 1.0);
11 |
12 | fragmentCol = vertexCol;
13 | }
14 |
--------------------------------------------------------------------------------
/graphics/js/src/main/scala/cwinter/codecraft/graphics/engine/GraphicsEngine.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | import org.scalajs.dom.{document, html}
4 |
5 | import scala.scalajs.js.timers.SetIntervalHandle
6 |
7 |
8 | private[codecraft] object GraphicsEngine {
9 | private[this] var intervalID: Option[SetIntervalHandle] = None
10 |
11 | def run(simulator: Simulator): Unit = {
12 | val canvas = document.getElementById("webgl-canvas").asInstanceOf[html.Canvas]
13 | val renderer = new WebGLRenderer(canvas, simulator)
14 | intervalID = Some(scala.scalajs.js.timers.setInterval(20.0) {
15 | renderer.render()
16 | simulator.run(1)
17 | })
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/graphics/js/src/main/scala/cwinter/codecraft/graphics/engine/JSRenderStack.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 |
4 | import org.scalajs.dom.raw.{WebGLRenderingContext => GL}
5 |
6 | import cwinter.codecraft.graphics.materials._
7 | import cwinter.codecraft.util.maths._
8 |
9 |
10 | private[graphics] case class JSRenderStack(implicit gl: GL) extends RenderStack {
11 | override val TranslucentAdditive = new TranslucentAdditive
12 | override val MaterialXYZRGB = new MaterialXYZRGB
13 | override val GaussianGlow = new GaussianGlow
14 | override val TranslucentProportional = new TranslucentAdditive // FIXME
15 | override val GaussianGlowPIntensity = new GaussianGlowPIntensity
16 | override val BloomShader = new MaterialBrightenedXYZRGB // FIXME
17 | override val TranslucentAdditivePIntensity = new TranslucentAdditivePIntensity
18 |
19 | override def dispose(): Unit = materials.foreach(_.dispose())
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/graphics/js/src/main/scala/cwinter/codecraft/graphics/engine/Renderer.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | trait Renderer {
4 | def render(): Unit
5 | }
6 |
--------------------------------------------------------------------------------
/graphics/js/src/main/scala/cwinter/codecraft/graphics/materials/GaussianGlow.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import cwinter.codecraft.util.CompileTimeLoader
4 | import org.scalajs.dom.raw.{WebGLRenderingContext => GL}
5 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
6 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
7 |
8 | private[graphics] class GaussianGlow(implicit gl: GL)
9 | extends JSMaterial[VertexXYZ, ColorRGBA, Unit](
10 | gl = gl,
11 | vsSource = CompileTimeLoader.loadResource("xyz_rgba_vs.glsl"),
12 | fsSource = CompileTimeLoader.loadResource("rgba_gaussian_fs.glsl"),
13 | "vertexPos",
14 | Some("vertexCol"),
15 | GL.BLEND
16 | ) {
17 |
18 | override def beforeDraw(projection: Matrix4x4): Unit = {
19 | super.beforeDraw(projection)
20 | gl.blendFunc(GL.SRC_ALPHA, GL.ONE)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/graphics/js/src/main/scala/cwinter/codecraft/graphics/materials/GaussianGlowPIntensity.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import cwinter.codecraft.graphics.model.VBO
4 | import cwinter.codecraft.util.CompileTimeLoader
5 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
6 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
7 | import org.scalajs.dom.raw.{WebGLRenderingContext => GL}
8 |
9 | private[graphics] class GaussianGlowPIntensity(implicit gl: GL)
10 | extends JSMaterial[VertexXYZ, ColorRGBA, Intensity](
11 | gl = gl,
12 | vsSource = CompileTimeLoader.loadResource("xyz_rgba_vs.glsl"),
13 | fsSource = CompileTimeLoader.loadResource("rgba_gaussian_pint_fs.glsl"),
14 | "vertexPos",
15 | Some("vertexCol"),
16 | GL.BLEND
17 | ) {
18 | val uniformIntensity = gl.getUniformLocation(programID, "intensity")
19 |
20 | override def beforeDraw(projection: Matrix4x4): Unit = {
21 | super.beforeDraw(projection)
22 | gl.blendFunc(GL.SRC_ALPHA, GL.ONE)
23 | }
24 |
25 | override def draw(vbo: VBO, modelview: Matrix4x4): Unit = {
26 | gl.uniform1f(uniformIntensity, params.intensity)
27 | super.draw(vbo, modelview)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/graphics/js/src/main/scala/cwinter/codecraft/graphics/materials/MaterialBrightenedXYZRGB.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import cwinter.codecraft.util.CompileTimeLoader
4 | import cwinter.codecraft.util.maths.{ColorRGB, VertexXYZ}
5 | import org.scalajs.dom.raw.{WebGLRenderingContext => GL}
6 |
7 |
8 | private[graphics] class MaterialBrightenedXYZRGB(implicit gl: GL)
9 | extends JSMaterial[VertexXYZ, ColorRGB, Unit](
10 | gl = gl,
11 | vsSource = CompileTimeLoader.loadResource("xyz_rgb_vs.glsl"),
12 | fsSource = CompileTimeLoader.loadResource("rgb1_brighten_fs.glsl"),
13 | "vertexPos",
14 | Some("vertexCol"),
15 | GL.DEPTH_TEST
16 | )
17 |
18 |
--------------------------------------------------------------------------------
/graphics/js/src/main/scala/cwinter/codecraft/graphics/materials/MaterialXYZRGB.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import cwinter.codecraft.util.CompileTimeLoader
4 | import cwinter.codecraft.util.maths.{ColorRGB, VertexXYZ}
5 | import org.scalajs.dom.raw.{WebGLRenderingContext => GL}
6 |
7 |
8 | private[graphics] class MaterialXYZRGB(implicit gl: GL)
9 | extends JSMaterial[VertexXYZ, ColorRGB, Unit](
10 | gl = gl,
11 | vsSource = CompileTimeLoader.loadResource("xyz_rgb_vs.glsl"),
12 | fsSource = CompileTimeLoader.loadResource("rgb1_fs.glsl"),
13 | "vertexPos",
14 | Some("vertexCol"),
15 | GL.DEPTH_TEST
16 | )
17 |
18 |
--------------------------------------------------------------------------------
/graphics/js/src/main/scala/cwinter/codecraft/graphics/materials/TranslucentAdditive.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import cwinter.codecraft.util.CompileTimeLoader
4 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
5 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
6 | import org.scalajs.dom.raw.{WebGLRenderingContext => GL}
7 |
8 |
9 | private[graphics] class TranslucentAdditive(implicit gl: GL)
10 | extends JSMaterial[VertexXYZ, ColorRGBA, Unit](
11 | gl = gl,
12 | vsSource = CompileTimeLoader.loadResource("xyz_rgba_vs.glsl"),
13 | fsSource = CompileTimeLoader.loadResource("rgba_fs.glsl"),
14 | "vertexPos",
15 | Some("vertexCol"),
16 | GL.BLEND
17 | ) {
18 |
19 | override def beforeDraw(projection: Matrix4x4): Unit = {
20 | super.beforeDraw(projection)
21 | gl.blendFunc(GL.SRC_ALPHA, GL.ONE)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/graphics/js/src/main/scala/cwinter/codecraft/graphics/materials/TranslucentAdditivePIntensity.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import cwinter.codecraft.util.CompileTimeLoader
4 | import org.scalajs.dom.raw.{WebGLRenderingContext => GL}
5 |
6 | import cwinter.codecraft.graphics.model.VBO
7 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
8 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
9 |
10 |
11 | private[graphics] class TranslucentAdditivePIntensity(implicit gl: GL)
12 | extends JSMaterial[VertexXYZ, ColorRGBA, Intensity](
13 | gl = gl,
14 | vsSource = CompileTimeLoader.loadResource("xyz_rgba_vs.glsl"),
15 | fsSource = CompileTimeLoader.loadResource("rgba_pint_fs.glsl"),
16 | "vertexPos",
17 | Some("vertexCol"),
18 | GL.BLEND
19 | ) {
20 | val uniformIntensity = gl.getUniformLocation(programID, "intensity")
21 |
22 |
23 | override def beforeDraw(projection: Matrix4x4): Unit = {
24 | super.beforeDraw(projection)
25 | gl.blendFunc(GL.SRC_ALPHA, GL.ONE)
26 | }
27 |
28 | override def draw(vbo: VBO, modelview: Matrix4x4): Unit = {
29 | gl.uniform1f(uniformIntensity, params.intensity)
30 | super.draw(vbo, modelview)
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/graphics/js/src/main/scala/cwinter/codecraft/graphics/model/JSVBO.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import org.scalajs.dom.raw.{WebGLBuffer, WebGLRenderingContext => GL}
4 |
5 | /**
6 | * Vertex Buffer Object
7 | */
8 | private[graphics] case class JSVBO(id: WebGLBuffer, size: Int) extends VBO {
9 | def withSize(size: Int): JSVBO = copy(size = size)
10 | override def dispose(anyGL: Any): Unit = {
11 | super.dispose(anyGL)
12 | assert(anyGL.isInstanceOf[GL], s"Expected gl of type ${GL.getClass.getName}. Actual: ${anyGL.getClass.getName}")
13 | val gl = anyGL.asInstanceOf[GL]
14 | gl.deleteBuffer(id)
15 | VBO._count -= 1
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/110_rgb1_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 110
2 |
3 |
4 | varying vec3 fragmentCol;
5 |
6 | void main(void) {
7 | gl_FragColor = vec4(fragmentCol, 1);
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/110_rgba_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 110
2 |
3 |
4 | varying vec4 fragmentCol;
5 |
6 | void main(void) {
7 | gl_FragColor = fragmentCol;
8 | }
9 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/110_rgba_gaussian_pint_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 110
2 |
3 | varying vec4 fragmentCol;
4 |
5 | uniform float intensity;
6 |
7 | void main() {
8 | float x = 1.0 - fragmentCol.w;
9 | float alpha = exp(-5.0 * x * x);
10 | gl_FragColor = vec4(fragmentCol.x * intensity, fragmentCol.y * intensity, fragmentCol.z * intensity, alpha);
11 | }
12 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/110_rgba_pint_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 110
2 |
3 | varying vec4 fragmentCol;
4 |
5 | uniform float intensity;
6 |
7 | void main() {
8 | gl_FragColor = intensity * fragmentCol;
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/110_xyz_rgb_vs.glsl:
--------------------------------------------------------------------------------
1 | #version 110
2 |
3 | attribute vec3 vertexPos;
4 | attribute vec3 vertexCol;
5 |
6 | varying vec3 fragmentCol;
7 |
8 | uniform mat4 modelview;
9 | uniform mat4 projection;
10 |
11 | void main (void) {
12 | gl_Position = projection * modelview * vec4(vertexPos, 1.0);
13 |
14 | fragmentCol = vertexCol;
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/110_xyz_rgba_vs.glsl:
--------------------------------------------------------------------------------
1 | #version 110
2 |
3 | attribute vec3 vertexPos;
4 | attribute vec4 vertexCol;
5 |
6 | varying vec4 fragmentCol;
7 |
8 | uniform mat4 modelview;
9 | uniform mat4 projection;
10 |
11 | void main (void) {
12 | gl_Position = projection * modelview * vec4(vertexPos, 1.0);
13 |
14 | fragmentCol = vertexCol;
15 | }
16 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/basic_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | out vec4 outputCol;
4 |
5 |
6 | void main () {
7 | outputCol = vec4(1.0, 0.0, 0.0, 0.0);
8 | }
9 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/basic_vs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec2 vertexPos;
4 |
5 | uniform mat4 projection;
6 | uniform mat4 modelview;
7 |
8 |
9 | void main () {
10 | gl_Position = projection * modelview * vec4(vertexPos, 0.0, 1.0);
11 | }
12 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/convolution_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec2 TexCoords;
4 | out vec4 color;
5 |
6 | uniform sampler2D screenTexture;
7 | uniform int orientation; // 0 for horizontal and 1 for vertical convolution
8 | uniform vec2 texelSize;
9 |
10 |
11 | float Gaussian(float x, float deviation) {
12 | return (1.0 / sqrt(2.0 * 3.141592 * deviation)) * exp(-((x * x) / (2.0 * deviation)));
13 | }
14 |
15 |
16 | void main()
17 | {
18 | float deviation = 200;
19 | float strength = 1;
20 | vec4 col = vec4(0, 0, 0, 0);
21 | int width = 25;
22 |
23 | if (orientation == 0) {
24 | for (int x = -width; x < width + 1; x++) {
25 | vec4 texcol = texture(screenTexture, TexCoords + vec2(texelSize.x * x, 0));
26 | if (texcol.w > 0)
27 | col += strength * Gaussian(x, deviation) * texcol;
28 | }
29 | } else if (orientation == 1) {
30 | for (int y = -width; y < width + 1; y++) {
31 | vec4 texcol = texture(screenTexture, TexCoords + vec2(0, texelSize.y * y));
32 | col += strength * Gaussian(y, deviation) * texcol;
33 | }
34 | }
35 |
36 | col.w = 0;// texture(screenTexture, TexCoords).w;
37 | color = col;
38 | }
39 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/rgb0_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec3 fragmentCol;
4 |
5 | out vec4 outputCol;
6 |
7 |
8 | void main() {
9 | outputCol = vec4(fragmentCol, 0);
10 | }
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/rgb1_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec3 fragmentCol;
4 |
5 | out vec4 outputCol;
6 |
7 |
8 | void main() {
9 | outputCol = vec4(fragmentCol, 1);
10 | }
11 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/rgba_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec4 fragmentCol;
4 |
5 | out vec4 outputCol;
6 |
7 | void main() {
8 | outputCol = fragmentCol;
9 | }
10 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/rgba_gaussian_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec4 fragmentCol;
4 |
5 | out vec4 outputCol;
6 |
7 |
8 | void main() {
9 | float x = 1 - fragmentCol.w;
10 | float alpha = exp(-5 * x * x);
11 | outputCol = vec4(fragmentCol.x, fragmentCol.y, fragmentCol.z, alpha);
12 | }
13 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/rgba_gaussian_pint_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec4 fragmentCol;
4 |
5 | out vec4 outputCol;
6 |
7 | uniform float intensity;
8 |
9 |
10 | void main() {
11 | float x = 1 - fragmentCol.w;
12 | float alpha = exp(-5 * x * x);
13 | outputCol = vec4(fragmentCol.x * intensity, fragmentCol.y * intensity, fragmentCol.z * intensity, alpha);
14 | }
15 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/rgba_pint_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec4 fragmentCol;
4 |
5 | uniform float intensity;
6 |
7 | out vec4 outputCol;
8 |
9 | vec4 srgb(vec4 rgba) {
10 | vec3 rgb = vec3(rgba.x, rgba.y, rgba.z);
11 | vec3 mask = vec3(greaterThan(rgb, vec3(0.0031308)));
12 | vec3 result =
13 | mix(rgb * 12.92,
14 | pow(rgb, vec3(1.0 / 2.4)) * 1.055 - 0.055,
15 | mask);
16 | return vec4(result, rgba.w);
17 | }
18 |
19 | void main() {
20 | outputCol = srgb(intensity * fragmentCol);
21 | }
22 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/texture_xy_fs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec2 TexCoords;
4 | out vec4 color;
5 |
6 | uniform sampler2D screenTexture;
7 |
8 | void main()
9 | {
10 | color = texture(screenTexture, TexCoords);
11 | }
12 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/texture_xy_vs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec2 vertexPos;
4 | in vec2 texCoords;
5 |
6 | out vec2 TexCoords;
7 |
8 | void main()
9 | {
10 | gl_Position = vec4(vertexPos.x, vertexPos.y, 0.0f, 1.0f);
11 | TexCoords = texCoords;
12 | }
13 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/xy_rgb_vs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec2 vertexPos;
4 | in vec3 vertexCol;
5 |
6 | out vec3 fragmentCol;
7 |
8 | uniform mat4 projection;
9 | uniform mat4 modelview;
10 |
11 |
12 | void main () {
13 | gl_Position = projection * modelview * vec4(vertexPos, 1.0, 1.0);
14 |
15 | fragmentCol = vertexCol;
16 | }
17 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/xyz_rgb_vs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec3 vertexPos;
4 | in vec3 vertexCol;
5 |
6 | out vec3 fragmentCol;
7 |
8 | uniform mat4 projection;
9 | uniform mat4 modelview;
10 |
11 |
12 | void main () {
13 | gl_Position = projection * modelview * vec4(vertexPos, 1.0);
14 |
15 | fragmentCol = vertexCol;
16 | }
17 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/resources/xyz_rgba_vs.glsl:
--------------------------------------------------------------------------------
1 | #version 400
2 |
3 | in vec3 vertexPos;
4 | in vec4 vertexCol;
5 |
6 | out vec4 fragmentCol;
7 |
8 | uniform mat4 projection;
9 | uniform mat4 modelview;
10 |
11 |
12 | void main () {
13 | gl_Position = projection * modelview * vec4(vertexPos, 1.0);
14 |
15 | fragmentCol = vertexCol;
16 | }
17 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/engine/GraphicsEngine.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | import cwinter.codecraft.graphics.application.DrawingCanvas
4 |
5 |
6 | private[codecraft] object GraphicsEngine {
7 | def run(simulator: Simulator): Unit = {
8 | DrawingCanvas.run(simulator)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/engine/JVMAsyncRunner.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | import java.util.concurrent.Executors
4 | import java.util.concurrent.atomic.AtomicInteger
5 |
6 | import scala.concurrent.ExecutionContext
7 | import scala.util.{Failure, Success}
8 |
9 | private[codecraft] trait JVMAsyncRunner { self: Simulator =>
10 | val id = JVMAsyncRunner.count.addAndGet(1)
11 |
12 | import JVMAsyncRunner._
13 |
14 | private[codecraft] def runAsync(): Unit = {
15 | if (stopped || gameStatus != Running) return
16 | performAsyncUpdate()(ec).onComplete {
17 | case Success(_) => runAsync()
18 | case Failure(x) => x.printStackTrace()
19 | }
20 | }
21 | }
22 |
23 | private[codecraft] object JVMAsyncRunner {
24 | val count = new AtomicInteger(0)
25 | private val threadPool = Executors.newFixedThreadPool(2048)
26 | implicit val ec = new ExecutionContext {
27 | override def reportFailure(cause: Throwable): Unit = { cause.printStackTrace() }
28 | override def execute(runnable: Runnable): Unit = threadPool.submit(runnable)
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/engine/JVMGL2RenderStack.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | import com.jogamp.opengl.GL2
4 |
5 | import cwinter.codecraft.graphics.materials._
6 |
7 |
8 | private[graphics] case class JVMGL2RenderStack(implicit gl: GL2) extends RenderStack {
9 |
10 | // materials
11 | val MaterialXYZRGB = new MaterialXYZRGB110
12 | val BloomShader = new MaterialXYZRGB110 // FIXME
13 | val GaussianGlow = new TranslucentAdditive110 // FIXME
14 | val GaussianGlowPIntensity = new GaussianGlowPIntensity110
15 | val TranslucentAdditive = new TranslucentAdditive110
16 | val TranslucentProportional = new TranslucentAdditive110 // FIXME
17 | val TranslucentAdditivePIntensity = new TranslucentAdditivePIntensity110
18 |
19 |
20 | override def dispose(): Unit = materials.foreach(_.dispose())
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/GaussianGlow.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import com.jogamp.opengl.GL4
4 | import com.jogamp.opengl.GL._
5 |
6 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
7 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
8 |
9 | private[graphics] class GaussianGlow(implicit gl: GL4)
10 | extends JVMMaterial[VertexXYZ, ColorRGBA, Unit](
11 | gl = gl,
12 | vsPath = "xyz_rgba_vs.glsl",
13 | fsPath = "rgba_gaussian_fs.glsl",
14 | "vertexPos",
15 | Some("vertexCol"),
16 | GL_BLEND
17 | ) {
18 | import gl._
19 |
20 | override def beforeDraw(projection: Matrix4x4): Unit = {
21 | super.beforeDraw(projection)
22 | glBlendFunc(GL_SRC_ALPHA, GL_ONE)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/GaussianGlowPIntensity.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import com.jogamp.opengl.GL._
4 | import com.jogamp.opengl.GL4
5 |
6 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
7 | import cwinter.codecraft.graphics.model.{VBO, JVMVBO$}
8 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
9 |
10 | private[graphics] class GaussianGlowPIntensity(implicit gl: GL4)
11 | extends JVMMaterial[VertexXYZ, ColorRGBA, Intensity](
12 | gl = gl,
13 | vsPath = "xyz_rgba_vs.glsl",
14 | fsPath = "rgba_gaussian_pint_fs.glsl",
15 | "vertexPos",
16 | Some("vertexCol"),
17 | GL_BLEND
18 | ) {
19 | import gl._
20 | val uniformIntensity = glGetUniformLocation(programID, "intensity")
21 |
22 |
23 | override def beforeDraw(projection: Matrix4x4): Unit = {
24 | super.beforeDraw(projection)
25 | glBlendFunc(GL_SRC_ALPHA, GL_ONE)
26 | }
27 |
28 | override def draw(vbo: VBO, modelview: Matrix4x4): Unit = {
29 | glUniform1f(uniformIntensity, params.intensity)
30 | super.draw(vbo, modelview)
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/GaussianGlowPIntensity110.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import com.jogamp.opengl.GL._
4 | import com.jogamp.opengl.GL2
5 |
6 | import cwinter.codecraft.graphics.model.VBO
7 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
8 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
9 |
10 | private[graphics] class GaussianGlowPIntensity110(implicit gl: GL2)
11 | extends JVMMaterial[VertexXYZ, ColorRGBA, Intensity](
12 | gl = gl,
13 | vsPath = "110_xyz_rgba_vs.glsl",
14 | fsPath = "110_rgba_gaussian_pint_fs.glsl",
15 | "vertexPos",
16 | Some("vertexCol"),
17 | GL_BLEND
18 | ) {
19 | import gl._
20 | val uniformIntensity = glGetUniformLocation(programID, "intensity")
21 |
22 |
23 | override def beforeDraw(projection: Matrix4x4): Unit = {
24 | super.beforeDraw(projection)
25 | glBlendFunc(GL_SRC_ALPHA, GL_ONE)
26 | }
27 |
28 | override def draw(vbo: VBO, modelview: Matrix4x4): Unit = {
29 | glUniform1f(uniformIntensity, params.intensity)
30 | super.draw(vbo, modelview)
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/MaterialXYZRGB.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import com.jogamp.opengl.GL._
4 | import com.jogamp.opengl.GL4
5 |
6 | import cwinter.codecraft.util.maths.{ColorRGB, VertexXYZ}
7 |
8 |
9 | private[graphics] class MaterialXYZRGB(implicit gl: GL4)
10 | extends JVMMaterial[VertexXYZ, ColorRGB, Unit](
11 | gl = gl,
12 | vsPath = "xyz_rgb_vs.glsl",
13 | fsPath = "rgb0_fs.glsl",
14 | "vertexPos",
15 | Some("vertexCol"),
16 | GL_DEPTH_TEST
17 | )
18 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/MaterialXYZRGB110.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import com.jogamp.opengl.GL._
4 | import com.jogamp.opengl.GL2
5 |
6 | import cwinter.codecraft.util.maths.{ColorRGB, VertexXYZ}
7 |
8 |
9 | private[graphics] class MaterialXYZRGB110(implicit gl: GL2)
10 | extends JVMMaterial[VertexXYZ, ColorRGB, Unit](
11 | gl = gl,
12 | vsPath = "110_xyz_rgb_vs.glsl",
13 | fsPath = "110_rgb1_fs.glsl",
14 | "vertexPos",
15 | Some("vertexCol"),
16 | GL_DEPTH_TEST
17 | )
18 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/RenderToScreen.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 |
4 | import com.jogamp.opengl.GL4
5 | import cwinter.codecraft.util.maths.VertexXY
6 |
7 |
8 | private[graphics] class RenderToScreen(implicit gl: GL4)
9 | extends JVMMaterial[VertexXY, VertexXY, Unit](
10 | gl = gl,
11 | vsPath = "texture_xy_vs.glsl",
12 | fsPath = "texture_xy_fs.glsl",
13 | "vertexPos",
14 | Some("texCoords")
15 | )
16 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/SimpleMaterial.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import com.jogamp.opengl.GL._
4 | import com.jogamp.opengl.GL4
5 |
6 | import cwinter.codecraft.util.maths.{EmptyVertex, VertexXY}
7 |
8 |
9 | private[graphics] class SimpleMaterial(implicit gl: GL4)
10 | extends JVMMaterial[VertexXY, EmptyVertex.type, Unit](
11 | gl = gl,
12 | vsPath = "basic_vs.glsl",
13 | fsPath = "basic_fs.glsl",
14 | "vertexPos",
15 | None,
16 | GL_DEPTH_TEST
17 | )
18 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/TranslucentAdditive.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import com.jogamp.opengl.GL._
4 | import com.jogamp.opengl.GL4
5 |
6 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
7 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
8 |
9 |
10 | private[graphics] class TranslucentAdditive(implicit gl: GL4)
11 | extends JVMMaterial[VertexXYZ, ColorRGBA, Unit](
12 | gl = gl,
13 | vsPath = "xyz_rgba_vs.glsl",
14 | fsPath = "rgba_fs.glsl",
15 | "vertexPos",
16 | Some("vertexCol"),
17 | GL_BLEND
18 | ) {
19 |
20 | import gl._
21 |
22 | override def beforeDraw(projection: Matrix4x4): Unit = {
23 | super.beforeDraw(projection)
24 | glBlendFunc(GL_SRC_ALPHA, GL_ONE)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/TranslucentAdditive110.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import com.jogamp.opengl.GL._
4 | import com.jogamp.opengl.GL2
5 |
6 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
7 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
8 |
9 |
10 | private[graphics] class TranslucentAdditive110(implicit gl: GL2)
11 | extends JVMMaterial[VertexXYZ, ColorRGBA, Unit](
12 | gl = gl,
13 | vsPath = "110_xyz_rgba_vs.glsl",
14 | fsPath = "110_rgba_fs.glsl",
15 | "vertexPos",
16 | Some("vertexCol"),
17 | GL_BLEND
18 | ) {
19 |
20 | import gl._
21 |
22 | override def beforeDraw(projection: Matrix4x4): Unit = {
23 | super.beforeDraw(projection)
24 | glBlendFunc(GL_SRC_ALPHA, GL_ONE)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/TranslucentAdditivePIntensity.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import com.jogamp.opengl.GL._
4 | import com.jogamp.opengl.GL4
5 |
6 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
7 | import cwinter.codecraft.graphics.model.{VBO, JVMVBO$}
8 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
9 |
10 |
11 | private[graphics] class TranslucentAdditivePIntensity(implicit gl: GL4)
12 | extends JVMMaterial[VertexXYZ, ColorRGBA, Intensity](
13 | gl = gl,
14 | vsPath = "xyz_rgba_vs.glsl",
15 | fsPath = "rgba_pint_fs.glsl",
16 | "vertexPos",
17 | Some("vertexCol"),
18 | GL_BLEND
19 | ) {
20 | import gl._
21 | val uniformIntensity = glGetUniformLocation(programID, "intensity")
22 |
23 |
24 | override def beforeDraw(projection: Matrix4x4): Unit = {
25 | super.beforeDraw(projection)
26 | glBlendFunc(GL_SRC_ALPHA, GL_ONE)
27 | }
28 |
29 | override def draw(vbo: VBO, modelview: Matrix4x4): Unit = {
30 | glUniform1f(uniformIntensity, params.intensity)
31 | super.draw(vbo, modelview)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/TranslucentAdditivePIntensity110.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import com.jogamp.opengl.GL._
4 | import com.jogamp.opengl.GL2
5 |
6 | import cwinter.codecraft.graphics.model.VBO
7 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
8 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
9 |
10 |
11 | private[graphics] class TranslucentAdditivePIntensity110(implicit gl: GL2)
12 | extends JVMMaterial[VertexXYZ, ColorRGBA, Intensity](
13 | gl = gl,
14 | vsPath = "110_xyz_rgba_vs.glsl",
15 | fsPath = "110_rgba_pint_fs.glsl",
16 | "vertexPos",
17 | Some("vertexCol"),
18 | GL_BLEND
19 | ) {
20 | import gl._
21 | val uniformIntensity = glGetUniformLocation(programID, "intensity")
22 |
23 |
24 | override def beforeDraw(projection: Matrix4x4): Unit = {
25 | super.beforeDraw(projection)
26 | glBlendFunc(GL_SRC_ALPHA, GL_ONE)
27 | }
28 |
29 | override def draw(vbo: VBO, modelview: Matrix4x4): Unit = {
30 | glUniform1f(uniformIntensity, params.intensity)
31 | super.draw(vbo, modelview)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/materials/TranslucentProportional.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | import com.jogamp.opengl.GL._
4 | import com.jogamp.opengl.GL4
5 |
6 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
7 | import cwinter.codecraft.util.maths.{ColorRGBA, VertexXYZ}
8 |
9 |
10 | private[graphics] class TranslucentProportional(implicit gl: GL4)
11 | extends JVMMaterial[VertexXYZ, ColorRGBA, Unit](
12 | gl = gl,
13 | vsPath = "xyz_rgba_vs.glsl",
14 | fsPath = "rgba_fs.glsl",
15 | "vertexPos",
16 | Some("vertexCol"),
17 | GL_BLEND
18 | ) {
19 | import gl._
20 |
21 | override def beforeDraw(projection: Matrix4x4): Unit = {
22 | super.beforeDraw(projection)
23 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/graphics/jvm/src/main/scala/cwinter/codecraft/graphics/model/JVMVBO.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import com.jogamp.opengl._
4 |
5 | /**
6 | * Vertex Buffer Object
7 | */
8 | private[graphics] case class JVMVBO(id: Int, size: Int, vao: Int) extends VBO {
9 | def withSize(size: Int): JVMVBO = copy(size = size)
10 | override def dispose(anyGL: Any): Unit = {
11 | super.dispose(anyGL)
12 | anyGL match {
13 | case gl2: GL2 =>
14 | gl2.glDeleteBuffers(1, Array(id), 0)
15 | gl2.glDeleteVertexArrays(1, Array(vao), 0)
16 | case gl4: GL4 =>
17 | gl4.glDeleteBuffers(1, Array(id), 0)
18 | gl4.glDeleteVertexArrays(1, Array(vao), 0)
19 | case _ =>
20 | throw new Exception(
21 | s"Expected gl of type javax.media.opengl.GL2 or javax.media.opengl.GL4. Actual: ${anyGL.getClass.getName}")
22 | }
23 | VBO._count -= 1
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/engine/Debug.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | import cwinter.codecraft.util.maths.{ColorRGBA, Vector2}
4 |
5 |
6 | private[codecraft] class Debug {
7 | private[this] var objects = List.empty[ModelDescriptor[_]]
8 | private[this] var _textModels = List.empty[TextModel]
9 | private[this] var activeObjects = List.empty[ModelDescriptor[_]]
10 | private[this] var activeTextModels = List.empty[TextModel]
11 |
12 | def draw(worldObject: ModelDescriptor[_]): Unit = objects ::= worldObject
13 |
14 | def drawText(text: TextModel): Unit = _textModels ::= text
15 |
16 | def drawText(
17 | text: String, xPos: Double, yPos: Double, color: ColorRGBA,
18 | absolutePosition: Boolean, centered: Boolean, largeFont: Boolean
19 | ): Unit = _textModels ::= TextModel(text, xPos.toFloat, yPos.toFloat, color, absolutePosition, centered, largeFont)
20 |
21 | def drawText(text: String, xPos: Double, yPos: Double, color: ColorRGBA): Unit =
22 | drawText(text, xPos, yPos, color, absolutePosition=false, centered = true, largeFont=false)
23 |
24 |
25 | private[this] var _cameraOverride: Option[() => Vector2] = None
26 | def setCameraOverride(getPos: => Vector2): Unit = {
27 | _cameraOverride = Some(() => getPos)
28 | }
29 |
30 | def cameraOverride: Option[Vector2] = _cameraOverride.map(_())
31 |
32 | private[engine] def debugObjects = activeObjects
33 |
34 | private[engine] def textModels = activeTextModels
35 |
36 | private[codecraft] def swapBuffers(): Unit = {
37 | activeObjects = objects
38 | activeTextModels = _textModels
39 | objects = List.empty[ModelDescriptor[_]]
40 | _textModels = List.empty[TextModel]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/engine/GraphicsContext.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | import cwinter.codecraft.graphics.model.{VBO, TheCompositeModelBuilderCache, TheModelCache}
4 |
5 | private[codecraft] class GraphicsContext(
6 | val materials: RenderStack,
7 | val useTransposedModelview: Boolean,
8 | private[graphics] val modelCache: TheModelCache,
9 | private[graphics] val modelBuilderCache: TheCompositeModelBuilderCache
10 | ) {
11 | private[this] var vbos = List.empty[VBO]
12 |
13 | private[graphics] def createdTempVBO(vbo: VBO): Unit = vbos ::= vbo
14 |
15 | private[graphics] def freeTempVBOs(gl: Any): Unit = {
16 | vbos.foreach(_.dispose(gl))
17 | vbos = List.empty[VBO]
18 | }
19 |
20 | private[graphics] def dispose(gl: Any): Unit = {
21 | freeTempVBOs(gl)
22 | materials.dispose()
23 | modelCache.clear()
24 | modelBuilderCache.clear()
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/engine/ModelDescriptor.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | import cwinter.codecraft.graphics.model.ClosedModel
4 | import cwinter.codecraft.util.maths.Rectangle
5 | import cwinter.codecraft.util.maths.matrices.{Matrix4x4, RotationZTranslationXYTransposedMatrix4x4, RotationZTranslationXYMatrix4x4}
6 |
7 |
8 | private[codecraft] case class ModelDescriptor[T](
9 | position: PositionDescriptor,
10 | objectDescriptor: WorldObjectDescriptor[T],
11 | objectParameters: T
12 | ) {
13 | @inline
14 | final def intersects(rectangle: Rectangle): Boolean =
15 | objectDescriptor.intersects(position.x, position.y, rectangle)
16 |
17 |
18 | def closedModel(timestep: Int, context: GraphicsContext): ClosedModel[T] =
19 | new ClosedModel[T](objectParameters, objectDescriptor.model(timestep, context), modelview(context))
20 |
21 | private def modelview(context: GraphicsContext): Matrix4x4 = {
22 | if (position.cachedModelviewMatrix.isEmpty) {
23 | val xPos = position.x
24 | val yPos = position.y
25 | val orientation = position.orientation
26 | val modelviewMatrix =
27 | if (context.useTransposedModelview) new RotationZTranslationXYTransposedMatrix4x4(orientation, xPos, yPos)
28 | else new RotationZTranslationXYMatrix4x4(orientation, xPos, yPos)
29 | position.cachedModelviewMatrix = modelviewMatrix
30 | }
31 | position.cachedModelviewMatrix.get
32 | }
33 | }
34 |
35 | private[codecraft] object ModelDescriptor {
36 | def apply(position: PositionDescriptor, objectDescriptor: WorldObjectDescriptor[Unit]): ModelDescriptor[Unit] =
37 | ModelDescriptor(position, objectDescriptor, Unit)
38 | }
39 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/engine/PositionDescriptor.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
4 |
5 |
6 | private[codecraft] case class PositionDescriptor(
7 | x: Float,
8 | y: Float,
9 | orientation: Float = 0
10 | ) {
11 | assert(!x.toDouble.isNaN)
12 | assert(!y.toDouble.isNaN)
13 | assert(!orientation.toDouble.isNaN)
14 |
15 | private[this] var _cachedModelviewMatrix: Option[Matrix4x4] = None
16 | private[graphics] def cachedModelviewMatrix_=(value: Matrix4x4): Unit =
17 | _cachedModelviewMatrix = Some(value)
18 | private[graphics] def cachedModelviewMatrix = _cachedModelviewMatrix
19 | }
20 |
21 | private[codecraft] object NullPositionDescriptor extends PositionDescriptor(0, 0, 0)
22 |
23 |
24 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/engine/RenderStack.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | import cwinter.codecraft.graphics.materials.{Intensity, Material}
4 | import cwinter.codecraft.util.PrecomputedHashcode
5 | import cwinter.codecraft.util.maths._
6 |
7 |
8 | private[codecraft] trait RenderStack extends PrecomputedHashcode {
9 | self: Product =>
10 | val MaterialXYZRGB: Material[VertexXYZ, ColorRGB, Unit]
11 | val BloomShader: Material[VertexXYZ, ColorRGB, Unit]
12 | val GaussianGlow: Material[VertexXYZ, ColorRGBA, Unit]
13 | val GaussianGlowPIntensity: Material[VertexXYZ, ColorRGBA, Intensity]
14 | val TranslucentAdditive: Material[VertexXYZ, ColorRGBA, Unit]
15 | val TranslucentProportional: Material[VertexXYZ, ColorRGBA, Unit]
16 | val TranslucentAdditivePIntensity: Material[VertexXYZ, ColorRGBA, Intensity]
17 |
18 | lazy val materials = List(
19 | MaterialXYZRGB, BloomShader, GaussianGlow, GaussianGlowPIntensity,
20 | TranslucentAdditive, TranslucentProportional, TranslucentAdditivePIntensity)
21 |
22 | private[graphics] def postDraw(camera2D: Camera2D): Unit = ()
23 |
24 | def dispose(): Unit
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/engine/TextModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | import cwinter.codecraft.util.maths.ColorRGBA
4 |
5 | private[codecraft] case class TextModel(
6 | text: String,
7 | xPos: Float,
8 | yPos: Float,
9 | color: ColorRGBA,
10 | absolutePos: Boolean = false,
11 | centered: Boolean = true,
12 | largeFont: Boolean = false
13 | )
14 |
15 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/engine/WorldObjectDescriptor.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.engine
2 |
3 | import cwinter.codecraft.graphics.model.{ClosedModel, Model}
4 | import cwinter.codecraft.util.PrecomputedHashcode
5 | import cwinter.codecraft.util.maths._
6 | import cwinter.codecraft.util.maths.matrices.{Matrix4x4, RotationZTranslationXYMatrix4x4, RotationZTranslationXYTransposedMatrix4x4}
7 |
8 |
9 | private[codecraft] trait WorldObjectDescriptor[T] extends PrecomputedHashcode {
10 | self: Product =>
11 |
12 | // making `ctx` private causes an issue with Scala.js
13 | // (it looks like `rs` gets inlined in subclasses, where `cxt` is not visible if private)
14 | protected var ctx: GraphicsContext = null
15 | implicit protected def rs: RenderStack = ctx.materials
16 |
17 | private var cachedModel = Option.empty[Model[T]]
18 |
19 |
20 | def intersects(xPos: Float, yPos: Float, rectangle: Rectangle): Boolean = true
21 |
22 | @inline
23 | final protected def intersects(x: Float, y: Float, width: Float, rectangle: Rectangle): Boolean = {
24 | x + width > rectangle.xMin &&
25 | x - width < rectangle.xMax &&
26 | y + width > rectangle.yMin &&
27 | y - width < rectangle.yMax
28 | }
29 |
30 | def model(timestep: Int, context: GraphicsContext): Model[T] = {
31 | cachedModel match {
32 | case Some(model) if ctx == context => model
33 | case _ =>
34 | ctx = context
35 | val model = getModel(context)
36 | if (allowCaching) cachedModel = Some(model)
37 | model
38 | }
39 | }
40 |
41 | protected def getModel(context: GraphicsContext): Model[T]
42 | protected def allowCaching: Boolean = true
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/materials/Intensity.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.materials
2 |
3 | private[codecraft] case class Intensity(intensity: Float) extends AnyVal
4 |
5 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/ClosedModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
4 |
5 |
6 | private[graphics] class ClosedModel[T](objectState: T, model: Model[T], modelview: Matrix4x4) {
7 | def draw(material: GenericMaterial): Unit = {
8 | if (model.hasMaterial(material)) {
9 | model.update(objectState)
10 | model.draw(modelview, material)
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/CompositeModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
4 |
5 | import scala.annotation.tailrec
6 |
7 |
8 | private[codecraft] case class CompositeModel[T](
9 | staticModels: Seq[Model[Unit]],
10 | dynamicModels: Seq[Model[T]]
11 | ) extends Model[T] {
12 | val models =
13 | if (staticModels.isEmpty) dynamicModels
14 | else if (dynamicModels.isEmpty) staticModels
15 | else staticModels ++ dynamicModels
16 |
17 |
18 | def update(params: T): Unit = for (model <- dynamicModels) model.update(params)
19 |
20 | def setVertexCount(n: Int): Unit = {
21 | require(n >= 0)
22 | _setVertexCount(n, models)
23 | }
24 |
25 | @tailrec
26 | private def _setVertexCount(remaining: Int, models: Seq[Model[_]]): Unit = models match {
27 | case Seq(model, _*) =>
28 | val count = model.vertexCount
29 | val allocated = math.min(count, remaining)
30 | model.setVertexCount(allocated)
31 | _setVertexCount(remaining - allocated, models.tail)
32 | case Seq() =>
33 | }
34 |
35 | // TODO: make more efficient (keep set of all materials?)
36 | def hasMaterial(material: GenericMaterial): Boolean =
37 | models.exists(_.hasMaterial(material))
38 |
39 | def draw(modelview: Matrix4x4, material: GenericMaterial): Unit =
40 | for {
41 | model <- models
42 | if model.hasMaterial(material)
43 | } model.draw(modelview, material)
44 |
45 | def vertexCount = models.map(_.vertexCount).sum
46 |
47 | def prettyPrintTree(depth: Int): String = {
48 | val root = prettyPrintNode(depth, s"Composite(${models.length})")
49 | val children =
50 | for (model <- models) yield model.prettyPrintTree(depth + 1)
51 |
52 | root + "\n" + children.mkString("\n")
53 | }
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/DecoratorModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
4 |
5 |
6 | private[graphics] trait DecoratorModel[T, U] extends Model[T] {
7 | protected def model: Model[U]
8 | protected def displayString: String
9 |
10 | override def setVertexCount(n: Int): Unit = model.setVertexCount(n)
11 | override def draw(modelview: Matrix4x4, material: GenericMaterial): Unit = model.draw(modelview, material)
12 | override def prettyPrintTree(depth: Int): String = prettyPrintWrapper(depth, displayString, model)
13 | override def vertexCount: Int = model.vertexCount
14 | override def hasMaterial(material: GenericMaterial): Boolean = model.hasMaterial(material)
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/DynamicModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 |
4 | private[graphics] class DynamicModel[T](
5 | val modelFactory: T => Model[Unit]
6 | ) extends DecoratorModel[T, Unit] {
7 | private[this] var _model: Model[Unit] = null
8 | def model: Model[Unit] = _model
9 |
10 | override protected def displayString: String = "Dynamic"
11 | override def hasMaterial(material: GenericMaterial): Boolean =
12 | model == null || model.hasMaterial(material)
13 | override def update(params: T): Unit =_model = modelFactory(params)
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/DynamicVertexCountModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.util.maths.Float0To1
4 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
5 |
6 |
7 | private[graphics] case class DynamicVertexCountModel[T](
8 | model: Model[T]
9 | ) extends DecoratorModel[(Float0To1, T), T] {
10 |
11 | override def update(params: (Float0To1, T)): Unit = {
12 | model.update(params._2)
13 | val targetVertexCount = (params._1 * vertexCount / 3).toInt * 3
14 | model.setVertexCount(targetVertexCount)
15 | }
16 |
17 | override def draw(modelview: Matrix4x4, material: GenericMaterial): Unit = {
18 | model.draw(modelview, material)
19 | setVertexCount(Integer.MAX_VALUE)
20 | }
21 |
22 | override protected def displayString: String = "DynamicVertexCount"
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/EmptyModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
4 |
5 |
6 | private[codecraft] class EmptyModel[T] extends Model[T] {
7 | def update(params: T) = ()
8 | def setVertexCount(n: Int) = ()
9 | def draw(modelview: Matrix4x4, material: GenericMaterial) = ()
10 | def hasMaterial(material: GenericMaterial) = false
11 | def vertexCount = 0
12 |
13 | def prettyPrintTree(depth: Int): String =
14 | prettyPrintNode(depth, "Empty")
15 | }
16 |
17 | private[codecraft] object EmptyModel extends EmptyModel[Unit]
18 |
19 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/EmptyModelBuilder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.graphics.engine.GraphicsContext
4 |
5 | private[codecraft] object EmptyModelBuilder extends ModelBuilder[Unit, Unit] {
6 |
7 | protected def buildModel(context: GraphicsContext) = EmptyModel
8 |
9 | override def isCacheable = false
10 |
11 | def signature = ()
12 | }
13 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/HideableModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
4 |
5 |
6 | private[graphics] class HideableModel[T](
7 | val model: Model[T]
8 | ) extends DecoratorModel[(IsHidden, T), T] {
9 | private[this] var show = true
10 |
11 | override def update(params: (IsHidden, T)): Unit = {
12 | val (isHidden, baseParams) = params
13 | show = !isHidden.value
14 | if (show) model.update(baseParams)
15 | }
16 |
17 | override def setVertexCount(n: Int): Unit =
18 | if (show) model.setVertexCount(n)
19 |
20 | override def draw(modelview: Matrix4x4, material: GenericMaterial): Unit =
21 | if (show) model.draw(modelview, material)
22 |
23 | override protected def displayString: String = "Hideable"
24 | }
25 |
26 | private[graphics] case class IsHidden(value: Boolean) extends AnyVal
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/IdentityModelviewModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.util.maths.matrices.{IdentityMatrix4x4, Matrix4x4}
4 |
5 |
6 | private[graphics] class IdentityModelviewModel[T](
7 | val model: Model[T]
8 | ) extends ParameterPreservingDecoratorModel[T] {
9 | override def draw(modelview: Matrix4x4, material: GenericMaterial): Unit =
10 | model.draw(IdentityMatrix4x4, material)
11 | override protected def displayString: String = "IdentityModelview"
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/ImmediateModeModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 |
4 | private[graphics] class ImmediateModeModel
5 | extends DecoratorModel[Seq[Model[Unit]], Unit] {
6 | private[this] var models = new StaticCompositeModel(Seq())
7 | def model: Model[Unit] = models
8 |
9 | override def update(params: Seq[Model[Unit]]): Unit =
10 | models = new StaticCompositeModel(params)
11 |
12 | override protected def displayString: String = "ImmediateMode"
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/Model.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.util.maths.VertexXYZ
4 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
5 |
6 |
7 | private[codecraft] trait Model[T] {
8 | def update(params: T): Unit
9 | def setVertexCount(n: Int): Unit
10 |
11 | def draw(modelview: Matrix4x4, material: GenericMaterial): Unit
12 |
13 | def hasMaterial(material: GenericMaterial): Boolean
14 |
15 | def vertexCount: Int
16 |
17 | def scalable(transpose: Boolean = false): ScalableModel[T] = new ScalableModel(this, transpose)
18 | def identityModelview: IdentityModelviewModel[T] = new IdentityModelviewModel[T](this)
19 | def translated(amount: VertexXYZ, transpose: Boolean): TranslatedModel[T] =
20 | new TranslatedModel[T](this, amount, transpose)
21 | def withDynamicVertexCount: DynamicVertexCountModel[T] = new DynamicVertexCountModel[T](this)
22 | def wireParameters[S](projection: S => T): ProjectedParamsModel[S, T] =
23 | new ProjectedParamsModel(this, projection)
24 |
25 | def prettyPrintTree(depth: Int): String
26 |
27 | def prettyTreeView: String = prettyPrintTree(0)
28 | protected def prettyPrintNode(depth: Int, contents: String): String = {
29 | if (depth == 0) contents
30 | else " " * (depth - 1) + "+--" + contents
31 | }
32 | protected def prettyPrintWrapper(depth: Int, contents: String, child: Model[_]): String = {
33 | val rootNode = prettyPrintNode(depth, contents)
34 | val childTree = child.prettyPrintTree(depth + 1)
35 | s"$rootNode\n$childTree"
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/ModelBuilder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.graphics.engine.GraphicsContext
4 |
5 |
6 | private[codecraft] trait ModelBuilder[TStatic, TDynamic] {
7 | def signature: TStatic
8 |
9 | def getModel(context: GraphicsContext): Model[TDynamic] =
10 | if (isCacheable) context.modelCache.getOrElseUpdate(signature)(optimized.buildModel(context))
11 | else buildModel(context)
12 |
13 | protected def buildModel(context: GraphicsContext): Model[TDynamic]
14 |
15 | def isCacheable: Boolean = true
16 |
17 | def optimized: ModelBuilder[TStatic, TDynamic] = this
18 |
19 | def wireParameters[SDynamic](projection: SDynamic => TDynamic) =
20 | ProjectedParamsModelBuilder(this, projection)
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/ParameterPreservingDecoratorModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 |
4 | private[graphics] trait ParameterPreservingDecoratorModel[T] extends DecoratorModel[T, T] {
5 | override def update(params: T): Unit = model.update(params)
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/PrimitiveModelBuilder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.graphics.engine.GraphicsContext
4 | import cwinter.codecraft.graphics.materials.Material
5 | import cwinter.codecraft.util.PrecomputedHashcode
6 | import cwinter.codecraft.util.maths.{Vertex, VertexXYZ}
7 |
8 | private[graphics] trait PrimitiveModelBuilder[
9 | TShape, TColor <: Vertex, TParams]
10 | extends ModelBuilder[TShape, TParams] with PrecomputedHashcode {
11 | self: Product =>
12 |
13 | val material: Material[VertexXYZ, TColor, TParams]
14 | val shape: TShape
15 | private[this] var _cacheable = true
16 | def signature = shape
17 |
18 | protected def buildModel(context: GraphicsContext): Model[TParams] = {
19 | val vbo = material.createVBO(computeVertexData(), !_cacheable)
20 | if (!_cacheable) context.createdTempVBO(vbo)
21 | new StaticModel(vbo, material)
22 | }
23 |
24 | override def isCacheable = _cacheable
25 | def noCaching: this.type = {
26 | _cacheable = false
27 | this
28 | }
29 |
30 | protected def computeVertexData(): Seq[(VertexXYZ, TColor)]
31 |
32 | def getVertexData: Seq[(VertexXYZ, TColor)] = computeVertexData()
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/ProjectedParamsModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 |
4 | private[graphics] case class ProjectedParamsModel[T, U](
5 | model: Model[U],
6 | projection: T => U
7 | ) extends DecoratorModel[T, U] {
8 | override def update(params: T) = model.update(projection(params))
9 | override protected def displayString: String = "ProjectParameters"
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/ProjectedParamsModelBuilder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.graphics.engine.GraphicsContext
4 |
5 | private[graphics] case class ProjectedParamsModelBuilder[
6 | TStatic, TDynamic, UDynamic](
7 | model: ModelBuilder[TStatic, UDynamic],
8 | projection: TDynamic => UDynamic
9 | ) extends ModelBuilder[TStatic, TDynamic] {
10 | def signature: TStatic = model.signature
11 |
12 | protected def buildModel(context: GraphicsContext): Model[TDynamic] =
13 | model.getModel(context).wireParameters(projection)
14 |
15 | override def isCacheable: Boolean = false
16 | }
17 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/ScalableModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.util.maths.matrices.{DilationXYMatrix4x4, Matrix4x4}
4 |
5 |
6 | private[graphics] class ScalableModel[T](
7 | val model: Model[T],
8 | transpose: Boolean = false
9 | ) extends DecoratorModel[(T, Float), T] {
10 |
11 | private[this] var scale = 1.0f
12 |
13 | def update(params: (T, Float)): Unit = {
14 | model.update(params._1)
15 | scale = params._2
16 | }
17 |
18 | override def draw(modelview: Matrix4x4, material: GenericMaterial): Unit = {
19 | val scaledModelview =
20 | if (transpose) modelview * new DilationXYMatrix4x4(scale)
21 | else new DilationXYMatrix4x4(scale) * modelview
22 | model.draw(scaledModelview, material)
23 | }
24 |
25 | override protected def displayString: String = "Scalable"
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/SimpleModelBuilder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.graphics.engine.GraphicsContext
4 |
5 | private[codecraft] trait SimpleModelBuilder[TStatic, TDynamic]
6 | extends ModelBuilder[TStatic, TDynamic] {
7 |
8 | protected def buildModel(context: GraphicsContext): Model[TDynamic] =
9 | model.getModel(context)
10 |
11 | protected def model: ModelBuilder[_, TDynamic]
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/StaticCompositeModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 |
4 | private[codecraft] class StaticCompositeModel(
5 | models: Seq[Model[Unit]]
6 | ) extends CompositeModel[Unit](models, Seq.empty)
7 |
8 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/StaticModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.graphics.materials.Material
4 | import cwinter.codecraft.util.maths.Vertex
5 | import cwinter.codecraft.util.maths.matrices.Matrix4x4
6 |
7 |
8 | private[graphics] class StaticModel[TPosition <: Vertex, TColor <: Vertex, TParams](
9 | val vbo: VBO,
10 | val material: Material[TPosition, TColor, TParams]
11 | ) extends Model[TParams] {
12 | private[this] var activeVertexCount = vbo.size
13 |
14 | def update(params: TParams): Unit = { material.params = params }
15 |
16 | def draw(modelview: Matrix4x4, material: GenericMaterial): Unit =
17 | if (material == this.material)
18 | material.draw(vbo.withSize(activeVertexCount), modelview)
19 |
20 | def hasMaterial(material: GenericMaterial): Boolean =
21 | this.material == material
22 |
23 | def setVertexCount(n: Int): Unit = {
24 | assert(n % 3 == 0)
25 | assert(n >= 0)
26 | assert(n <= vertexCount)
27 | activeVertexCount = n
28 | }
29 | def vertexCount = vbo.size
30 |
31 |
32 | def prettyPrintTree(depth: Int): String =
33 | prettyPrintNode(depth, s"Static[${material.getClass.getSimpleName}]($vertexCount)")
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/TheCompositeModelBuilderCache.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | private[codecraft] class TheCompositeModelBuilderCache extends TypedCache {
4 | type V[a] = (Seq[ModelBuilder[_, Unit]], Seq[ModelBuilder[_, a]])
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/TheModelCache.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 |
4 | private[codecraft] class TheModelCache extends TypedCache {
5 | type V[a] = Model[a]
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/TranslatedModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.util.maths.VertexXYZ
4 | import cwinter.codecraft.util.maths.matrices.{TranslationMatrix4x4, DilationXYMatrix4x4, Matrix4x4}
5 |
6 |
7 | private[graphics] class TranslatedModel[T](
8 | val model: Model[T],
9 | translation: VertexXYZ,
10 | transpose: Boolean = false
11 | ) extends ParameterPreservingDecoratorModel[T] {
12 |
13 | import translation._
14 | private val translationMatrix =
15 | if (transpose) new TranslationMatrix4x4(x, y, z).transposed
16 | else new TranslationMatrix4x4(x, y, z)
17 |
18 | override def draw(modelview: Matrix4x4, material: GenericMaterial): Unit = {
19 | val translatedModelview =
20 | if (transpose) modelview * translationMatrix
21 | else translationMatrix * modelview
22 | model.draw(translatedModelview, material)
23 | }
24 |
25 | override protected def displayString: String = "Translated"
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/TypedCache.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 |
4 | import scala.language.higherKinds
5 |
6 |
7 | private[graphics] trait TypedCache {
8 | type V[a]
9 |
10 | private[this] var nModels = 0
11 | private[this] val cache = new java.util.HashMap[Any, Any]()
12 | private[this] var _lastCachedModel = ""
13 |
14 |
15 | def get[T](key: Any): Option[V[T]] = {
16 | cache.get(key).asInstanceOf[Option[V[T]]]
17 | }
18 |
19 | def getOrElseUpdate[T](key: Any)(generator: => V[T]): V[T] = {
20 | val result = cache.get(key)
21 | if (result == null) {
22 | nModels += 1
23 | _lastCachedModel = key.toString
24 | val value = generator
25 | cache.put(key, value)
26 | value
27 | } else result
28 | }.asInstanceOf[V[T]]
29 |
30 | def CachedModelCount = nModels
31 |
32 | def lastCachedModel = _lastCachedModel
33 |
34 | def clear(): Unit = {
35 | cache.clear()
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/VBO.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 |
4 | private[graphics] trait VBO {
5 | val size: Int
6 | private[this] var _disposed = false
7 | def disposed: Boolean = _disposed
8 | def withSize(size: Int): VBO
9 | def dispose(gl: Any): Unit = _disposed = true
10 | }
11 |
12 | private[graphics] object VBO {
13 | var _count = 0
14 | def count = _count
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/VertexCollectionModelBuilder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.model
2 |
3 | import cwinter.codecraft.graphics.engine.GraphicsContext
4 | import cwinter.codecraft.graphics.materials.Material
5 | import cwinter.codecraft.util.maths.{Vertex, VertexXYZ}
6 |
7 |
8 | private[graphics] case class VertexCollectionModelBuilder[TColor <: Vertex](
9 | vertexData: Seq[Seq[(VertexXYZ, TColor)]],
10 | material: Material[VertexXYZ, TColor, Unit]
11 | ) extends ModelBuilder[Nothing, Unit] {
12 |
13 | def signature = throw new Exception("ConcreteVerticesModelBuilder has no signature.")
14 |
15 | protected def buildModel(context: GraphicsContext): Model[Unit] = {
16 | val vbo = material.createVBOSeq(vertexData, dynamic=false)
17 | new StaticModel(vbo, material)
18 | }
19 |
20 | override def isCacheable = false
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/model/package.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics
2 |
3 | import cwinter.codecraft.graphics.materials.Material
4 | import cwinter.codecraft.util.maths.Vertex
5 |
6 | import scala.language.existentials
7 |
8 | package object model {
9 | type GenericMaterial = Material[_ <: Vertex, _ <: Vertex, _]
10 | }
11 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/models/CircleModelBuilder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.models
2 |
3 | import cwinter.codecraft.graphics.engine.{GraphicsContext, WorldObjectDescriptor}
4 | import cwinter.codecraft.graphics.model.{Model, ModelBuilder}
5 | import cwinter.codecraft.graphics.primitives.Polygon
6 | import cwinter.codecraft.util.maths.ColorRGB
7 |
8 |
9 | private[graphics] case class CircleModelBuilder(radius: Float, id: Int)
10 | extends ModelBuilder[CircleModelBuilder, Unit] with WorldObjectDescriptor[Unit] {
11 | val ColorCode = false
12 |
13 | override protected def buildModel(context: GraphicsContext): Model[Unit] = {
14 | Polygon(
15 | rs.MaterialXYZRGB,
16 | n = 50,
17 | colorMidpoint = if (ColorCode) Colors(id / 10) else ColorRGB(0.0f, 0.0f, 0.4f),
18 | colorOutside = if (ColorCode) Colors(id % 10) else ColorRGB(0.7f, 0.7f, 0.7f),
19 | radius = radius,
20 | zPos = 0
21 | ).getModel(context)
22 | }
23 |
24 |
25 | val Colors = IndexedSeq(
26 | ColorRGB(0, 0, 0),
27 | ColorRGB(0.5f, 0.5f, 0.5f),
28 | ColorRGB(1, 1, 1),
29 |
30 | ColorRGB(1, 0, 0),
31 | ColorRGB(0, 1, 0),
32 | ColorRGB(0, 0, 1),
33 |
34 | ColorRGB(0.75f, 0.75f, 0),
35 | ColorRGB(0.75f, 0, 0.75f),
36 | ColorRGB(0, 0.75f, 0.75f),
37 |
38 | ColorRGB(0.5f, 0, 0)
39 | )
40 |
41 | override def signature = this
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/models/CircleOutlineModelBuilder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.models
2 |
3 | import cwinter.codecraft.graphics.engine.WorldObjectDescriptor
4 | import cwinter.codecraft.graphics.model.{SimpleModelBuilder, Model, ModelBuilder}
5 | import cwinter.codecraft.graphics.primitives.PolygonRing
6 | import cwinter.codecraft.util.maths.{ColorRGB, VertexXY}
7 |
8 |
9 | private[codecraft] case class CircleOutlineModelBuilder(
10 | radius: Float,
11 | color: ColorRGB = ColorRGB(1, 1, 1)
12 | ) extends SimpleModelBuilder[CircleOutlineModelBuilder, Unit] with WorldObjectDescriptor[Unit] {
13 |
14 | override protected def model =
15 | new PolygonRing(
16 | rs.MaterialXYZRGB, 40, Seq.fill(40)(color), Seq.fill(40)(color),
17 | radius - 2, radius, VertexXY(0, 0), 0, 0
18 | ).noCaching
19 |
20 |
21 | override def signature = this
22 | override def isCacheable = false
23 | override def allowCaching = false
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/models/RectangleModelBuilder.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.models
2 |
3 | import cwinter.codecraft.graphics.engine.{GraphicsContext, WorldObjectDescriptor}
4 | import cwinter.codecraft.graphics.model.{Model, SimpleModelBuilder}
5 | import cwinter.codecraft.graphics.primitives.RectanglePrimitive
6 | import cwinter.codecraft.util.maths
7 | import cwinter.codecraft.util.maths.ColorRGB
8 |
9 |
10 | private[codecraft] case class RectangleModelBuilder(rectangle: maths.Rectangle)
11 | extends SimpleModelBuilder[RectangleModelBuilder, Unit] with WorldObjectDescriptor[Unit] {
12 |
13 | override protected def model =
14 | RectanglePrimitive(
15 | rs.MaterialXYZRGB,
16 | rectangle.xMin.toFloat,
17 | rectangle.xMax.toFloat,
18 | rectangle.yMin.toFloat,
19 | rectangle.yMax.toFloat,
20 | 3,
21 | ColorRGB(0.7f, 0.7f, 0.7f),
22 | 0
23 | )
24 |
25 | override def signature = this
26 | }
27 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/models/TestModel.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.models
2 |
3 | import cwinter.codecraft.graphics.engine.{GraphicsContext, WorldObjectDescriptor}
4 | import cwinter.codecraft.graphics.model._
5 |
6 |
7 | private[graphics] case class TestModel(t: Int)
8 | extends ModelBuilder[TestModel, Unit] with WorldObjectDescriptor[Unit] {
9 | val sideLength = 50
10 |
11 | protected def buildModel(context: GraphicsContext): Model[Unit] = EmptyModel
12 |
13 | override def isCacheable: Boolean = false
14 | override def signature = this
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/primitives/LinePrimitive.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.primitives
2 |
3 | import cwinter.codecraft.graphics.materials.Material
4 | import cwinter.codecraft.graphics.model.PrimitiveModelBuilder
5 | import cwinter.codecraft.util.maths.{Vertex, VertexXY, VertexXYZ}
6 |
7 | import scala.reflect.ClassTag
8 |
9 |
10 | private[graphics] case class LinePrimitive[TColor <: Vertex : ClassTag, TParams](
11 | material: Material[VertexXYZ, TColor, TParams],
12 | p1: VertexXY,
13 | p2: VertexXY,
14 | width: Float,
15 | colorInside: TColor,
16 | colorOutside: TColor,
17 | zPos: Float
18 | ) extends PrimitiveModelBuilder[LinePrimitive[TColor, TParams], TColor, TParams] {
19 | val shape = this
20 |
21 | protected override def computeVertexData(): Seq[(VertexXYZ, TColor)] = {
22 | val offset = width * (p1 - p2).perpendicular.normalized
23 |
24 | val upperLeft = p1 + offset
25 | val upperRight = p1 - offset
26 | val downLeft = p2 + offset
27 | val downRight = p2 - offset
28 |
29 | val vertexPos =
30 | Seq(
31 | upperLeft, downLeft, p1,
32 | p1, downLeft, p2,
33 | p1, p2, upperRight,
34 | upperRight, p2, downRight
35 | )
36 |
37 | val colors = Seq(
38 | colorOutside, colorOutside, colorInside,
39 | colorInside, colorOutside, colorInside,
40 | colorInside, colorInside, colorOutside,
41 | colorOutside, colorInside, colorOutside
42 | )
43 |
44 | vertexPos.map(_.zPos(zPos)) zip colors
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/primitives/PolygonWave.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.primitives
2 |
3 | import cwinter.codecraft.graphics.materials.Material
4 | import cwinter.codecraft.graphics.model.PrimitiveModelBuilder
5 | import cwinter.codecraft.util.maths.{NullVectorXY, Vertex, VertexXY, VertexXYZ}
6 |
7 | import scala.reflect.ClassTag
8 |
9 |
10 | private[graphics] case class PolygonWave[TColor <: Vertex : ClassTag, TParams](
11 | material: Material[VertexXYZ, TColor, TParams],
12 | n: Int,
13 | colorMidpoint: TColor,
14 | colorOutside: TColor,
15 | cycle: Int,
16 | radius: Float = 1,
17 | position: VertexXY = NullVectorXY,
18 | zPos: Float = 0,
19 | orientation: Float = 0
20 | ) extends PrimitiveModelBuilder[PolygonWave[TColor, TParams], TColor, TParams] {
21 | val shape = this
22 |
23 | assert(cycle >= 0)
24 | assert(cycle <= 100)
25 |
26 | protected override def computeVertexData(): Seq[(VertexXYZ, TColor)] = {
27 | val vertices =
28 | for {
29 | i <- 0 until n
30 | offset = (1 - cycle / 100f) * 2 * math.sin(16 * math.Pi.toFloat * (i / n.toFloat)).toFloat
31 | } yield (radius + offset) * VertexXY(i * 2 * math.Pi.toFloat / n + orientation) + position
32 |
33 | val vertexPos = new Array[VertexXYZ](3 * n)
34 | for (i <- 0 until n) {
35 | val v1 = if (i == 0) vertices(n - 1) else vertices(i - 1)
36 | val v2 = vertices(i)
37 |
38 | vertexPos(3 * i + 0) = position.zPos(zPos)
39 | vertexPos(3 * i + 1) = v1.zPos(zPos)
40 | vertexPos(3 * i + 2) = v2.zPos(zPos)
41 | }
42 |
43 |
44 | val colors = new Array[TColor](vertexPos.length)
45 | for (i <- 0 until n) {
46 | colors(3 * i + 1) = colorOutside
47 | colors(3 * i + 2) = colorOutside
48 | colors(3 * i) = colorMidpoint
49 | }
50 |
51 | vertexPos zip colors
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/graphics/shared/src/main/scala/cwinter/codecraft/graphics/primitives/SquarePrimitive.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.graphics.primitives
2 |
3 | import cwinter.codecraft.graphics.materials.Material
4 | import cwinter.codecraft.graphics.model.PrimitiveModelBuilder
5 | import cwinter.codecraft.util.maths.{Vertex, VertexXYZ}
6 |
7 | import scala.reflect.ClassTag
8 |
9 |
10 | private[codecraft] case class SquarePrimitive[TColor <: Vertex : ClassTag, TParams](
11 | material: Material[VertexXYZ, TColor, TParams],
12 | midpointX: Float,
13 | midpointY: Float,
14 | width: Float,
15 | color: TColor,
16 | zPos: Float
17 | ) extends PrimitiveModelBuilder[SquarePrimitive[TColor, TParams], TColor, TParams] {
18 | val shape = this
19 |
20 | protected override def computeVertexData(): Seq[(VertexXYZ, TColor)] = {
21 | val p1 = VertexXYZ(midpointX + width, midpointY + width, zPos)
22 | val p2 = VertexXYZ(midpointX - width, midpointY + width, zPos)
23 | val p3 = VertexXYZ(midpointX - width, midpointY - width, zPos)
24 | val p4 = VertexXYZ(midpointX + width, midpointY - width, zPos)
25 |
26 | val vertexPos =
27 | Seq(
28 | p1, p2, p3,
29 | p1, p3, p4
30 | )
31 |
32 | val colors = vertexPos.map(_ => color)
33 |
34 | vertexPos zip colors
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/physics/shared/src/test/scala/cwinter/codecraft/physics/ConstantVelocity$Test.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.physics
2 |
3 | import cwinter.codecraft.util.maths.Vector2
4 | import org.scalatest.FlatSpec
5 |
6 |
7 | class ConstantVelocity$Test extends FlatSpec {
8 | "calculateCollisionTime" should "asdf" in {
9 | val p1 = Vector2(-100, 0)
10 | val v1 = Vector2(50, 0)
11 | val p2 = Vector2(100, 0)
12 | val v2 = Vector2(-50, 0)
13 | val obj1 = new ConstantVelocityObject(p1, v1, 1, 50)
14 | val obj2 = new ConstantVelocityObject(p2, v2, 1, 50)
15 | assertResult(Some(1))(obj1.computeCollisionTime(obj2, 2))
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/project/Commons.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | import Keys._
3 |
4 | object Commons {
5 | val appVersion = "0.7.0"
6 |
7 | val settings: Seq[Def.Setting[_]] = Seq(
8 | organization := "org.codecraftgame",
9 | version := appVersion,
10 | scalaVersion := "2.11.8",
11 | scalacOptions := Seq(
12 | "-Xlint",
13 | "-deprecation",
14 | "-Xfatal-warnings",
15 | "-feature"
16 | ),
17 | autoAPIMappings := true,
18 | publishArtifact in Test := false,
19 | publishTo := {
20 | val nexus = "https://oss.sonatype.org/"
21 | if (isSnapshot.value) Some("snapshots" at nexus + "content/repositories/snapshots")
22 | else Some("releases" at nexus + "service/local/staging/deploy/maven2")
23 | },
24 | pomIncludeRepository := { _ =>
25 | false
26 | },
27 | pomExtra :=
28 | http://www.codecraftgame.org
29 |
30 |
31 | MIT License
32 | http://www.opensource.org/licenses/mit-license.php
33 |
34 |
35 |
36 | git@github.com:cswinter/CodeCraftGame.git
37 | scm:git:git@github.com:cswinter/CodeCraftGame.git
38 |
39 |
40 |
41 | cswinter
42 | Clemens Winter
43 | https://github.com/cswinter
44 | http://www.codecraftgame.org
45 |
46 |
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/project/Dependencies.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 |
3 | object Dependencies {
4 | // libraryDependencies += groupID % artifactID % revision
5 | val gluegen = "org.jogamp.gluegen" % "gluegen-rt-main" % "2.3.2"
6 | val jogl = "org.jogamp.jogl" % "jogl-all-main" % "2.3.2"
7 | val scalaSwing = "org.scala-lang.modules" % "scala-swing_2.11" % "1.0.1"
8 | val jodaTime = "joda-time" % "joda-time" % "2.7"
9 | val jodaConvert = "org.joda" % "joda-convert" % "1.2"
10 | val scalatest = "org.scalatest" % "scalatest_2.11" % "2.2.1" % "test"
11 | val sprayWebsocket = "com.wandoulabs.akka" %% "spray-websocket" % "0.1.4"
12 | val akka = "com.typesafe.akka" %% "akka-actor" % "2.3.13"
13 | val javaxWebsocket = "javax.websocket" % "javax.websocket-client-api" % "1.1"
14 | val javaxWebsocketImpl = "org.glassfish.tyrus" % "tyrus-container-grizzly-client" % "1.13"
15 | val jetm = "fm.void.jetm" % "jetm" % "1.2.3"
16 | val async = "org.scala-lang.modules" % "scala-async_2.11" % "0.9.5"
17 | val scalaXml = "org.scala-lang.modules" %% "scala-xml" % "1.0.4"
18 |
19 |
20 | val commonDependencies: Seq[ModuleID] = Seq(
21 | scalatest,
22 | scalaXml
23 | )
24 |
25 | val coreJVMDependencies: Seq[ModuleID] = Seq(
26 | sprayWebsocket,
27 | akka,
28 | javaxWebsocket,
29 | javaxWebsocketImpl,
30 | jetm
31 | )
32 |
33 | val graphicsJVMDependencies: Seq[ModuleID] = Seq(
34 | gluegen,
35 | jogl,
36 | scalaSwing,
37 | jodaTime,
38 | jodaConvert,
39 | async
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/project/assembly.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.13.0")
2 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version = 0.13.16
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | logLevel := Level.Warn
2 |
3 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.11")
4 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
5 |
6 |
--------------------------------------------------------------------------------
/testai/src/main/scala/cwinter/codecraft/testai/RunBenchmark.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.testai
2 |
3 | import cwinter.codecraft.core.api._
4 | import cwinter.codecraft.core.game.DroneWorldSimulator
5 |
6 | object RunTestAI {
7 | def main(args: Array[String]): Unit = {
8 | while (true) {
9 | val startTime = System.nanoTime()
10 | var frames = 0L
11 |
12 | for (_ <- 0 to 10) {
13 | val controllers = Seq(TheGameMaster.destroyerAI(), TheGameMaster.replicatorAI())
14 | val simulator = new DroneWorldSimulator(TheGameMaster.defaultMap.createGameConfig(controllers))
15 | simulator.graphicsEnabled = false
16 | simulator.run(steps = 1000000)
17 | frames += simulator.timestep
18 | }
19 |
20 | val elapsed = System.nanoTime() - startTime
21 | println(s"Ran $frames frames in ${elapsed / 1000 / 1000}ms (${frames * 1000000000 / elapsed}FPS)")
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/testai/src/main/scala/cwinter/codecraft/testai/RunLastReplay.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.testai
2 |
3 | import cwinter.codecraft.core.api.TheGameMaster
4 |
5 |
6 | object RunLastReplay {
7 | def main(args: Array[String]): Unit = {
8 | TheGameMaster.runLastReplay()
9 | }
10 | }
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/testai/src/main/scala/cwinter/codecraft/testai/RunTestAI.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.testai
2 |
3 | import cwinter.codecraft.core.api._
4 |
5 | object RunTestAI {
6 | def main(args: Array[String]): Unit = {
7 | TheGameMaster.runGame(TheGameMaster.destroyerAI(), TheGameMaster.replicatorAI())
8 | }
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/util/jvm/src/main/scala/scala/scalajs/js/annotation/JSExport.scala:
--------------------------------------------------------------------------------
1 | package scala.scalajs.js.annotation
2 |
3 | class JSExport extends scala.annotation.StaticAnnotation {
4 | def this(name: String) = this()
5 | }
6 |
--------------------------------------------------------------------------------
/util/jvm/src/main/scala/scala/scalajs/js/annotation/JSExportAll.scala:
--------------------------------------------------------------------------------
1 | package scala.scalajs.js.annotation
2 |
3 | class JSExportAll extends scala.annotation.StaticAnnotation
4 |
5 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/AggregateStatistics.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util
2 |
3 |
4 | private[codecraft] class AggregateStatistics {
5 | private val emaDecay = 0.9
6 | private[this] var _count = 0
7 | private[this] var _total = 0.0
8 | private[this] var _ema = 0.0
9 | private[this] var _last = 0.0
10 |
11 | def addMeasurement(measurement: Double): Unit = {
12 | _count += 1
13 | _ema = emaDecay * _ema + (1 - emaDecay) * measurement
14 | _total += measurement
15 | _last = measurement
16 | }
17 |
18 | def average = _total / _count
19 | def count = _count
20 | def ema = _ema
21 | def last = _last
22 | def total = _total
23 |
24 | def display: String = f"Last: $last%.4g Average: $average%.4g EMA: $ema%.4g Count: $count"
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/CompileTimeLoader.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util
2 |
3 | import scala.io.Source
4 | import scala.language.experimental.macros
5 | import scala.reflect.macros.blackbox
6 |
7 |
8 | /**
9 | * Macro to import file contents as a string at compile time.
10 | * To make this work with sbt, add this line to the settings:
11 | * `unmanagedClasspath in Compile <++= unmanagedResources in Compile`
12 | */
13 | private[cwinter] object CompileTimeLoader {
14 | def loadResource(path: String): String = macro loadResourceImpl
15 |
16 | def loadResourceImpl(c: blackbox.Context)(path: c.Expr[String]) = {
17 | import c.universe._
18 | path.tree match {
19 | case Literal(Constant(s: String)) =>
20 | val stream = this.getClass.getResourceAsStream("/" + s)
21 | if (stream == null) c.abort(c.enclosingPosition, "Couldn't get shader resource: " + s)
22 | Literal(Constant(Source.fromInputStream(stream).mkString))
23 | case _ =>
24 | c.abort(c.enclosingPosition, "Need a literal path!")
25 | }
26 | }
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/PrecomputedHashcode.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util
2 |
3 | import scala.runtime.ScalaRunTime
4 |
5 |
6 | private[cwinter] trait PrecomputedHashcode {
7 | self: Product =>
8 | override lazy val hashCode: Int = ScalaRunTime._hashCode(this)
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/Float0To1.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths
2 |
3 | import scala.language.implicitConversions
4 |
5 | private[codecraft] class Float0To1 private (val value: Float) extends AnyVal
6 |
7 | private[codecraft] case object Float0To1 {
8 | def apply(value: Float): Float0To1 = {
9 | require(value >= 0)
10 | require(value <= 1)
11 | new Float0To1(value)
12 | }
13 |
14 | implicit def float0To1IsFloat(float0To1: Float0To1): Float = {
15 | float0To1.value
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/Functions.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths
2 |
3 | import scala.math._
4 |
5 | private[codecraft] object Functions {
6 | def gaussian(x: Double): Double =
7 | sqrt(2 * Pi) * exp(-x * x)
8 | }
9 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/Geometry.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths
2 |
3 |
4 | private[codecraft] object Geometry {
5 | import math._
6 |
7 | def polygonVertices(
8 | n: Int,
9 | orientation: Float = 0,
10 | radius: Float = 1,
11 | position: VertexXY = NullVectorXY
12 | ): IndexedSeq[VertexXY] = {
13 |
14 | assert(n >= 3)
15 | assert(radius > 0)
16 |
17 | for (i <- 0 until n)
18 | yield radius * VertexXY(i * 2 * math.Pi.toFloat / n + orientation) + position
19 | }
20 |
21 | def polygonVertices2(
22 | n: Int,
23 | orientation: Float = 0,
24 | radius: Float = 1,
25 | position: VertexXY = NullVectorXY
26 | ): IndexedSeq[VertexXY] = {
27 | val angle0 = (Pi + math.Pi / n).toFloat
28 | polygonVertices(n, orientation + angle0, radius, position)
29 | }
30 |
31 |
32 | /**
33 | * Computes the inradius of a regular polygon given the radius.
34 | * @param radius The radius.
35 | */
36 | def inradius(radius: Float, n: Int): Float =
37 | radius * cos(Pi / n).toFloat
38 |
39 | /**
40 | * Computes the circumradius of a regular polygon given the inradius.
41 | * @param inradius The inradius.
42 | */
43 | def circumradius(inradius: Float, n: Int): Float =
44 | inradius / cos(Pi / n).toFloat
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/RNG.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths
2 |
3 | private[codecraft] class RNG(
4 | private[this] var _seed: Int
5 | ) {
6 | def this() = this(scala.util.Random.nextInt())
7 |
8 | private[this] var random = new scala.util.Random(seed)
9 |
10 |
11 | def seed: Int = _seed
12 | def seed_=(value: Int): Unit = {
13 | _seed = value
14 | random = new scala.util.Random(seed)
15 | }
16 |
17 | def int(): Int = random.nextInt()
18 |
19 | def int(max: Int): Int = random.nextInt(max)
20 |
21 | def int(min: Int, max: Int): Int = {
22 | assert(min <= max)
23 | random.nextInt(max - min + 1) + min
24 | }
25 |
26 | def bernoulli(p: Double): Boolean = {
27 | assert(p >= 0)
28 | assert(p <= 1)
29 | random.nextDouble() <= p
30 | }
31 |
32 | def vector2(size: Double = 1): Vector2 = {
33 | val direction = 2 * math.Pi * random.nextFloat()
34 | size * Vector2(direction)
35 | }
36 |
37 |
38 | def vector2(xMin: Double, xMax: Double, yMin: Double, yMax: Double): Vector2 =
39 | Vector2(double(xMin, xMax), double(yMin, yMax))
40 |
41 | def vector2(bounds: Rectangle): Vector2 =
42 | vector2(bounds.xMin, bounds.xMax, bounds.yMin, bounds.yMax)
43 |
44 | def float(min: Float, max: Float): Float = double(min, max).toFloat
45 |
46 | def double(): Double = random.nextDouble()
47 |
48 | def gaussian2D(): Vector2 = Vector2(random.nextGaussian(), random.nextGaussian())
49 |
50 | def double(min: Double, max: Double): Double = {
51 | assert(min <= max)
52 | random.nextDouble() * (max - min) + min
53 | }
54 | }
55 |
56 | private[codecraft] object GlobalRNG extends RNG
57 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/Rectangle.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths
2 |
3 | import scala.scalajs.js.annotation.JSExportAll
4 |
5 | /**
6 | * Defines an axis aligned rectangle positioned in 2D space.
7 | * @param xMin The x coordinate for the left side of the rectangle.
8 | * @param xMax The x coordinate for the right hand side of the rectangle.
9 | * @param yMin The y coordinate of the bottom side of the rectangle.
10 | * @param yMax The y coordinate of the top side of the rectangle.
11 | */
12 | @JSExportAll
13 | final case class Rectangle(xMin: Double, xMax: Double, yMin: Double, yMax: Double) {
14 | assert(xMin < xMax)
15 | assert(yMin < yMax)
16 |
17 |
18 | /** Returns true if `point` is inside the rectangle. */
19 | def contains(point: Vector2): Boolean =
20 | point.x >= xMin && point.x <= xMax && point.y >= yMin && point.y <= yMax
21 |
22 |
23 | /** Returns the width. */
24 | def width: Double = xMax - xMin
25 |
26 | /** Returns the height. */
27 | def height: Double = yMax - yMin
28 |
29 | /** Returns true if this rectangle and `that` overlap. */
30 | def intersects(that: Rectangle): Boolean = !(
31 | this.xMin > that.xMax ||
32 | this.xMax < that.xMin ||
33 | this.yMin > that.yMax ||
34 | this.yMax < that.yMin
35 | )
36 | }
37 |
38 |
39 | private[codecraft] object Rectangle {
40 | private final val RectangleRegex = """Rectangle\((.*),(.*),(.*),(.*)\)""".r
41 | def fromString(string: String): Rectangle = {
42 | val RectangleRegex(xMin, xMax, yMin, yMax) = string
43 | Rectangle(xMin.toDouble, xMax.toDouble, yMin.toDouble, yMax.toDouble)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/Solve.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths
2 |
3 | private[codecraft] object Solve {
4 | /**
5 | * Returns the smallest strictly positive solution of ax**2 + bx + c.
6 | * If there is no such solution, returns None
7 | */
8 | def quadratic(a: Double, b: Double, c: Double): Option[Double] = {
9 | val determinant = b * b - 4 * a * c
10 |
11 | if (determinant < 0) return None
12 |
13 | val f = 1.0 / (2 * a)
14 | val t1 = f * (-b + Math.sqrt(determinant))
15 | val t2 = f * (-b - Math.sqrt(determinant))
16 |
17 | if (t1 <= 0 && t2 <= 0) None
18 | else if (t1 <= 0) Some(t2)
19 | else if (t2 <= 0) Some(t1)
20 | else Some(Math.min(t1, t2))
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/matrices/DilationMatrix4x4.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths.matrices
2 |
3 | private[codecraft] class DilationMatrix4x4(x: Float, y: Float, z: Float) extends Matrix4x4(
4 | Array[Float](
5 | x, 0, 0, 0,
6 | 0, y, 0, 0,
7 | 0, 0, z, 0,
8 | 0, 0, 0, 1
9 | )
10 | )
11 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/matrices/DilationXYMatrix4x4.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths.matrices
2 |
3 | private[codecraft] class DilationXYMatrix4x4(x: Float, y: Float) extends Matrix4x4(
4 | Array[Float](
5 | x, 0, 0, 0,
6 | 0, y, 0, 0,
7 | 0, 0, 1, 0,
8 | 0, 0, 0, 1
9 | )
10 | ) {
11 | def this(xy: Float) = this(xy, xy)
12 | }
13 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/matrices/IdentityMatrix4x4.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths.matrices
2 |
3 | private[codecraft] object IdentityMatrix4x4 extends Matrix4x4(
4 | Array[Float](
5 | 1, 0, 0, 0,
6 | 0, 1, 0, 0,
7 | 0, 0, 1, 0,
8 | 0, 0, 0, 1
9 | )
10 | )
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/matrices/Matrix2x2.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths.matrices
2 |
3 | import cwinter.codecraft.util.maths.VertexXY
4 |
5 |
6 | private[codecraft] final case class Matrix2x2(m11: Float, m12: Float, m21: Float, m22: Float) {
7 | def *(other: Matrix2x2): Matrix2x2 =
8 | Matrix2x2(
9 | m11 * other.m11 + m12 * other.m21, m11 * other.m12 + m12 * other.m22,
10 | m12 * other.m11 + m22 * other.m21, m12 * other.m21 + m22 * other.m22
11 | )
12 |
13 | def *(other: VertexXY): VertexXY =
14 | VertexXY(
15 | m11 * other.x + m12 * other.y,
16 | m21 * other.x + m22 * other.y
17 | )
18 | }
19 |
20 |
21 | private[codecraft] object Matrix2x2 {
22 | def rotation(angle: Float): Matrix2x2 = {
23 | val cos = math.cos(angle).toFloat
24 | val sin = math.sin(angle).toFloat
25 | Matrix2x2(
26 | cos, -sin,
27 | sin, cos
28 | )
29 | }
30 |
31 | def scale(s: Float): Matrix2x2 =
32 | Matrix2x2(
33 | s, 0,
34 | 0, s
35 | )
36 |
37 | def scale(x: Float, y: Float): Matrix2x2 =
38 | Matrix2x2(
39 | x, 0,
40 | 0, y
41 | )
42 |
43 | def scaleX(x: Float): Matrix2x2 =
44 | Matrix2x2(
45 | x, 0,
46 | 0, 1
47 | )
48 |
49 | def scaleY(y: Float): Matrix2x2 =
50 | Matrix2x2(
51 | 1, 0,
52 | 0, y
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/matrices/OrthographicProjectionMatrix4x4.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths.matrices
2 |
3 | private[codecraft] class OrthographicProjectionMatrix4x4(right: Float, left: Float, top: Float, bottom: Float,
4 | near: Float = 0.0f, far: Float = 1.0f) extends Matrix4x4({
5 | val rml = right - left
6 | val rpl = right + left
7 | val tmb = top - bottom
8 | val tpb = top + bottom
9 | val fmn = far - near
10 | val fpn = far + near
11 | Array[Float](
12 | 2 / rml, 0, 0, -rpl / rml,
13 | 0, 2 / tmb, 0, -tpb / tmb,
14 | 0, 0, -2/fmn, fpn / fmn,
15 | 0, 0, 0, 1
16 | )}
17 | )
18 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/matrices/RotationZMatrix4x4.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths.matrices
2 |
3 | private[codecraft] class RotationZMatrix4x4(angle: Double) extends Matrix4x4({
4 | val cos = math.cos(angle).toFloat
5 | val sin = math.sin(angle).toFloat
6 | Array[Float](
7 | cos, -sin, 0, 0,
8 | sin, cos, 0, 0,
9 | 0, 0, 1, 0,
10 | 0, 0, 0, 1
11 | )
12 | })
13 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/matrices/RotationZTranslationXYMatrix4x4.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths.matrices
2 |
3 | private[codecraft] class RotationZTranslationXYMatrix4x4(angle: Double, x: Float, y: Float) extends Matrix4x4({
4 | val cos = math.cos(angle).toFloat
5 | val sin = math.sin(angle).toFloat
6 | Array[Float](
7 | cos, -sin, 0, x,
8 | sin, cos, 0, y,
9 | 0 , 0 , 1, 0,
10 | 0 , 0 , 0, 1
11 | )
12 | })
13 |
14 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/matrices/RotationZTranslationXYTransposedMatrix4x4.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths.matrices
2 |
3 | private[codecraft] class RotationZTranslationXYTransposedMatrix4x4(angle: Double, x: Float, y: Float) extends Matrix4x4({
4 | val cos = math.cos(angle).toFloat
5 | val sin = math.sin(angle).toFloat
6 | Array[Float](
7 | cos, sin, 0, 0,
8 | -sin, cos, 0, 0,
9 | 0 , 0 , 1, 0,
10 | x , y , 0, 1
11 | )
12 | })
13 |
14 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/matrices/TranslationMatrix4x4.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths.matrices
2 |
3 | private[codecraft] class TranslationMatrix4x4(x: Float, y: Float, z: Float) extends Matrix4x4(
4 | Array[Float](
5 | 1, 0, 0, x,
6 | 0, 1, 0, y,
7 | 0, 0, 1, z,
8 | 0, 0, 0, 1
9 | )
10 | )
11 |
--------------------------------------------------------------------------------
/util/shared/src/main/scala/cwinter/codecraft/util/maths/matrices/TranslationXYMatrix4x4.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths.matrices
2 |
3 | private[codecraft] class TranslationXYMatrix4x4(x: Float, y: Float) extends Matrix4x4(
4 | Array[Float](
5 | 1, 0, 0, x,
6 | 0, 1, 0, y,
7 | 0, 0, 1, 0,
8 | 0, 0, 0, 1
9 | )
10 | )
11 |
--------------------------------------------------------------------------------
/util/shared/src/test/scala/cwinter/codecraft/util/maths/Matrix4x4Test.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths
2 |
3 | import cwinter.codecraft.util.maths.matrices.{IdentityMatrix4x4, Matrix4x4}
4 | import org.scalatest.FlatSpec
5 |
6 |
7 | class Matrix4x4Test extends FlatSpec {
8 | val A = new Matrix4x4(Array[Float](
9 | 10.4f, 15.1f, -123.4f, 0,
10 | -5, -43.4f, 1234101f, 43,
11 | 1, 0, 34.545f, 431,
12 | 114.0f, 452.4f, -123, -1
13 | ))
14 |
15 | val B = new Matrix4x4(Array[Float](
16 | 1, 4, 12, 94,
17 | -12, 43, 12, 10,
18 | -4, -5, -6, 12,
19 | 12, 34, 51, 10
20 | ))
21 |
22 |
23 | "IdentityMatrix4x4" should "have value 1 on the diagonal and 0 otherwise" in {
24 | for (row <- 0 to 3; col <- 0 to 3)
25 | if (row == col) assert(IdentityMatrix4x4(row, col) === 1)
26 | else assert(IdentityMatrix4x4(row, col) === 0)
27 | }
28 |
29 | it should "respect the identity I * I = I" in {
30 | assert(IdentityMatrix4x4 * IdentityMatrix4x4 === IdentityMatrix4x4)
31 | }
32 |
33 | it should "respect the identity I * A = A" in {
34 | assert(IdentityMatrix4x4 * A === A)
35 | }
36 |
37 | it should "respect the identity A * I = A" in {
38 | assert(A * IdentityMatrix4x4 === A)
39 | }
40 |
41 |
42 | "robowars.graphics.matrices.Matrix4x4" should "respect the identity (B * B) * (B * B) === B * (B * (B * B)))" in {
43 | assert((B * B) * (B * B) === B * (B * (B * B)))
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/util/shared/src/test/scala/cwinter/codecraft/util/maths/Solve$Test.scala:
--------------------------------------------------------------------------------
1 | package cwinter.codecraft.util.maths
2 |
3 | import org.scalatest.FlatSpec
4 |
5 |
6 | class Solve$Test extends FlatSpec {
7 | "Solve.quadratic" should "solve x**2 - 1 = 0" in {
8 | assertResult(Some(1))(Solve.quadratic(1, 0, -1))
9 | }
10 |
11 | it should "find no (positive) solution for x**2 + 1 = 0" in {
12 | assertResult(None)(Solve.quadratic(1, 0, 1))
13 | }
14 |
15 | it should "solve x**2 - 2*x + 1 = 0" in {
16 | assertResult(Some(1))(Solve.quadratic(1, -2, 1))
17 | }
18 |
19 | it should "solve x**2 - 6*x + 9" in {
20 | assertResult(Some(3))(Solve.quadratic(1, -6, 9))
21 | }
22 | }
23 |
--------------------------------------------------------------------------------