adding destroyer

This commit is contained in:
evanpelle
2024-11-10 18:28:21 -08:00
committed by Evan
parent 6a1a09c335
commit c7951d77c0
16 changed files with 249 additions and 42 deletions
+1 -1
View File
@@ -175,7 +175,7 @@
* add gold DONE 11/4/2024 * add gold DONE 11/4/2024
* add troop/worker slider DONE 11/4/2024 * add troop/worker slider DONE 11/4/2024
* create Unit layer DONE 11/9/2024 * create Unit layer DONE 11/9/2024
* create Unit interface * create Unit interface DONE 11/10/2024
* add destroyer * add destroyer
* NPC has relations * NPC has relations
* use twitter emojis * use twitter emojis
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
shape-rendering="geometricPrecision"
text-rendering="geometricPrecision"
image-rendering="optimizeQuality"
fill-rule="evenodd"
clip-rule="evenodd"
viewBox="0 0 500 499.99999"
version="1.1"
id="svg44"
sodipodi:docname="Destroyer.svg"
width="500"
height="500"
inkscape:export-filename="warship-icon.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs48" />
<sodipodi:namedview
id="namedview46"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.2877175"
inkscape:cx="86.587311"
inkscape:cy="266.36276"
inkscape:window-width="1536"
inkscape:window-height="987"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg44" />
<path
d="M 5.8295895,247.16089 C 65.39826,256.02013 106.88513,262.31167 162.00809,266.85556 l 14.35936,-93.47237 c 5.31364,0 10.70442,-0.0538 16.15307,-0.12099 v -59.97127 h 17.56104 V 172.953 l 12.17026,-0.25542 v -34.25394 h 17.56104 v 33.94474 c 19.59584,-0.26887 39.07597,-0.2151 57.44707,1.0217 11.50485,1.3309 21.51493,6.54696 29.54807,16.89843 7.85955,10.13636 13.56858,24.99139 16.59668,45.68087 l 0.10608,0.73939 0.0193,0.79316 0.58827,39.24146 c 21.23526,0.68562 28.57406,1.27713 50.74475,1.80142 l 97.12095,2.33917 c 1.9673,0.0403 4.58072,2.67524 3.57778,4.98752 l -29.90488,68.79018 c -0.99329,2.28538 -1.64906,4.98752 -3.57778,4.98752 H 50.305987 c -2.237321,0 -43.357737,-96.22828 -48.0541823,-107.50734 -0.7811336,-1.86864 0.5496866,-5.4446 3.5777848,-5.00097 z M 108.38954,189.01793 H 98.5434 l -1.330822,-16.25314 c -0.125367,-1.41156 -0.858282,-2.58114 -1.861219,-2.58114 H 71.531605 c -1.002937,0 -1.735852,1.16958 -1.851576,2.58114 l -1.340464,16.25314 H 59.4385 c -1.40797,0 -2.218034,1.72077 -2.594136,3.60285 l -0.829351,4.18092 -43.12629,-9.90783 -1.475475,10.29769 41.438656,15.6213 -6.422654,32.73482 c 25.989569,3.45497 51.352302,6.42597 76.38715,9.00712 l -11.84237,-61.93402 c -0.35681,-1.90897 -1.20545,-3.60285 -2.58449,-3.60285 z m 283.25255,37.11739 h 9.8365 l 1.34046,-16.25314 c 0.11572,-1.41156 0.839,-2.58114 1.85158,-2.58114 h 23.81975 c 1.02222,0 1.7455,1.16958 1.86122,2.58114 l 1.34046,16.25314 h 8.90107 c 1.42726,0 2.21803,1.72077 2.58449,3.60285 l 0.82935,4.18092 36.30825,-7.34013 1.47547,10.29769 -34.62061,13.0536 4.45536,22.65222 -56.76238,-1.35779 -13.67466,-0.33608 7.85955,-41.15043 c 0.36646,-1.90897 1.16688,-3.60285 2.59414,-3.60285 z m -121.095,-21.68429 h 52.28774 c 4.69644,9.86749 8.04278,19.95009 9.04572,30.28811 h -61.33346 z m -35.60426,-0.0135 h 22.49857 v 31.0275 h -22.49857 z"
id="path42"
style="stroke-width:1.13861" />
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

+59
View File
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
shape-rendering="geometricPrecision"
text-rendering="geometricPrecision"
image-rendering="optimizeQuality"
fill-rule="evenodd"
clip-rule="evenodd"
viewBox="0 0 500 499.99999"
version="1.1"
id="svg44"
sodipodi:docname="DestroyerIconWhite.svg"
width="500"
height="500"
inkscape:export-filename="warship-icon.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs48" />
<sodipodi:namedview
id="namedview46"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.2877175"
inkscape:cx="86.587311"
inkscape:cy="266.36276"
inkscape:window-width="1536"
inkscape:window-height="987"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg44" />
<path
d="M 5.8295895,247.16089 C 65.39826,256.02013 106.88513,262.31167 162.00809,266.85556 l 14.35936,-93.47237 c 5.31364,0 10.70442,-0.0538 16.15307,-0.12099 v -59.97127 h 17.56104 V 172.953 l 12.17026,-0.25542 v -34.25394 h 17.56104 v 33.94474 c 19.59584,-0.26887 39.07597,-0.2151 57.44707,1.0217 11.50485,1.3309 21.51493,6.54696 29.54807,16.89843 7.85955,10.13636 13.56858,24.99139 16.59668,45.68087 l 0.10608,0.73939 0.0193,0.79316 0.58827,39.24146 c 21.23526,0.68562 28.57406,1.27713 50.74475,1.80142 l 97.12095,2.33917 c 1.9673,0.0403 4.58072,2.67524 3.57778,4.98752 l -29.90488,68.79018 c -0.99329,2.28538 -1.64906,4.98752 -3.57778,4.98752 H 50.305987 c -2.237321,0 -43.357737,-96.22828 -48.0541823,-107.50734 -0.7811336,-1.86864 0.5496866,-5.4446 3.5777848,-5.00097 z M 108.38954,189.01793 H 98.5434 l -1.330822,-16.25314 c -0.125367,-1.41156 -0.858282,-2.58114 -1.861219,-2.58114 H 71.531605 c -1.002937,0 -1.735852,1.16958 -1.851576,2.58114 l -1.340464,16.25314 H 59.4385 c -1.40797,0 -2.218034,1.72077 -2.594136,3.60285 l -0.829351,4.18092 -43.12629,-9.90783 -1.475475,10.29769 41.438656,15.6213 -6.422654,32.73482 c 25.989569,3.45497 51.352302,6.42597 76.38715,9.00712 l -11.84237,-61.93402 c -0.35681,-1.90897 -1.20545,-3.60285 -2.58449,-3.60285 z m 283.25255,37.11739 h 9.8365 l 1.34046,-16.25314 c 0.11572,-1.41156 0.839,-2.58114 1.85158,-2.58114 h 23.81975 c 1.02222,0 1.7455,1.16958 1.86122,2.58114 l 1.34046,16.25314 h 8.90107 c 1.42726,0 2.21803,1.72077 2.58449,3.60285 l 0.82935,4.18092 36.30825,-7.34013 1.47547,10.29769 -34.62061,13.0536 4.45536,22.65222 -56.76238,-1.35779 -13.67466,-0.33608 7.85955,-41.15043 c 0.36646,-1.90897 1.16688,-3.60285 2.59414,-3.60285 z m -121.095,-21.68429 h 52.28774 c 4.69644,9.86749 8.04278,19.95009 9.04572,30.28811 h -61.33346 z m -35.60426,-0.0135 h 22.49857 v 31.0275 h -22.49857 z"
id="path42"
style="stroke-width:1.13861" />
<path
style="fill:#ffffff;stroke-width:0.776568"
d="M 46.706349,351.9808 C 42.004196,342.57569 31.558258,319.09983 15.001906,280.72927 4.2738049,255.8661 2.3933801,250.94457 3.0592429,249.47241 c 0.4346644,-0.961 1.4320088,-1.74728 2.2163209,-1.74728 0.7843122,0 15.7310272,2.08277 33.2149232,4.62839 55.538666,8.08629 122.159113,15.92117 123.550193,14.53008 0.23554,-0.23552 3.6233,-21.24116 7.52837,-46.67918 l 7.10013,-46.25095 7.95982,-0.001 7.95982,-0.001 V 143.66505 113.3789 h 8.54224 8.54225 v 29.89786 29.89786 h 6.60083 6.60082 v -17.08449 -17.08449 h 8.15396 8.15397 v 16.69621 16.6962 l 13.39579,0.0102 c 21.89937,0.0167 46.22637,1.33987 51.05934,2.77709 18.87306,5.61245 30.73872,21.45768 37.4049,49.94994 1.26524,5.40782 1.67375,10.89917 2.15857,29.0159 0.32889,12.29014 0.73487,22.48072 0.90216,22.64572 0.54568,0.53819 41.93742,1.93217 95.43373,3.21399 54.05235,1.29515 55.99853,1.44928 55.87254,4.42493 -0.0222,0.52405 -7.2771,17.55195 -16.12202,37.83978 l -16.08168,36.88697 -206.35685,0.19558 -206.356855,0.19559 -3.786166,-7.57298 z M 257.82051,220.15698 V 204.23734 H 246.172 234.52348 v 15.91964 15.91964 h 11.64852 11.64851 z m 74.1798,11.84265 c -0.98753,-6.42557 -3.63791,-15.80032 -6.09246,-21.54975 l -2.48647,-5.82426 -26.58789,-0.20434 -26.58789,-0.20434 v 15.54156 15.54155 h 31.13097 31.13098 z"
id="path370" />
<path
style="fill:#ffffff;stroke-width:0.776568"
d="m 107.94293,252.66328 c -5.33891,-0.60164 -20.99574,-2.48304 -34.792965,-4.18087 -13.797226,-1.69784 -25.366829,-3.08698 -25.710229,-3.08698 -0.343401,0 0.8512,-7.35153 2.654669,-16.33673 2.940969,-14.65242 3.149735,-16.40042 2.02491,-16.95453 -0.689767,-0.3398 -10.071533,-3.90876 -20.848367,-7.93102 -14.89771,-5.56031 -19.520201,-7.59241 -19.285331,-8.47806 0.169902,-0.64067 0.525567,-2.76568 0.790368,-4.72225 l 0.481454,-3.5574 19.580444,4.50115 c 10.769244,2.47564 20.492947,4.70513 21.608227,4.95443 1.844631,0.41233 2.069192,0.19808 2.486266,-2.37205 0.70519,-4.34557 1.657226,-5.01642 7.119029,-5.01642 h 4.842078 l 0.550514,-8.70006 c 0.302782,-4.78504 0.722598,-8.9785 0.932924,-9.31882 0.210326,-0.34031 6.104255,-0.61875 13.097618,-0.61875 14.76188,0 13.249343,-1.15028 14.040604,10.67781 l 0.532477,7.95982 h 5.664 5.664 l 0.80464,2.52384 c 0.80804,2.53444 11.74089,59.3751 11.74089,61.04166 0,1.06925 -1.42288,1.03009 -13.97822,-0.38477 z"
id="path372" />
<path
style="fill:#ffffff;stroke-width:0.776568"
d="m 416.88697,271.41698 c -18.65086,-0.4307 -34.15559,-1.02828 -34.45497,-1.32794 -0.53305,-0.53356 6.25145,-38.17574 7.4887,-41.54929 0.55729,-1.51951 1.26654,-1.7384 6.29062,-1.94141 l 5.66252,-0.22882 0.77657,-8.54225 c 0.42711,-4.69823 1.05929,-8.97906 1.40483,-9.51295 0.45782,-0.70738 3.87376,-0.97071 12.5919,-0.97071 10.68101,0 12.04305,0.14836 12.70426,1.38384 0.40734,0.76112 0.9915,5.12931 1.29815,9.7071 l 0.55754,8.32325 h 5.2935 5.29349 l 0.80874,2.91213 c 0.44481,1.60167 0.81069,3.35561 0.81307,3.89764 0.003,0.6267 0.64048,0.83439 1.75161,0.57047 4.27407,-1.01523 32.80247,-6.60367 33.71102,-6.60367 1.00175,0 1.31418,0.90275 2.17005,6.26999 l 0.44256,2.77543 -17.28817,6.44808 c -9.5085,3.54644 -17.34584,6.49096 -17.4163,6.54338 -0.0705,0.0524 0.89062,5.1332 2.13575,11.29063 1.24513,6.15743 2.17643,11.22747 2.06954,11.26676 -0.10688,0.0393 -15.45412,-0.28096 -34.10498,-0.71166 z"
id="path374" />
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

+1 -1
View File
@@ -1,5 +1,5 @@
import { Executor } from "../core/execution/ExecutionManager"; import { Executor } from "../core/execution/ExecutionManager";
import { Cell, MutableGame, PlayerEvent, PlayerID, MutablePlayer, TileEvent, Player, Game, BoatEvent, Tile, PlayerType, GameMap, Difficulty } from "../core/game/Game"; import { Cell, MutableGame, PlayerEvent, PlayerID, MutablePlayer, TileEvent, Player, Game, UnitEvent, Tile, PlayerType, GameMap, Difficulty } from "../core/game/Game";
import { createGame } from "../core/game/GameImpl"; import { createGame } from "../core/game/GameImpl";
import { EventBus } from "../core/EventBus"; import { EventBus } from "../core/EventBus";
import { Config, getConfig } from "../core/configuration/Config"; import { Config, getConfig } from "../core/configuration/Config";
+19 -2
View File
@@ -1,7 +1,7 @@
import { Config } from "../core/configuration/Config" import { Config } from "../core/configuration/Config"
import { EventBus, GameEvent } from "../core/EventBus" import { EventBus, GameEvent } from "../core/EventBus"
import { AllianceRequest, AllPlayers, Cell, Player, PlayerID, PlayerType } from "../core/game/Game" import { AllianceRequest, AllPlayers, Cell, Player, PlayerID, PlayerType, Tile } from "../core/game/Game"
import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientLeaveMessageSchema, GameID, Intent, ServerMessage, ServerMessageSchema } from "../core/Schemas" import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientLeaveMessageSchema, CreateDestroyerIntent, GameID, Intent, ServerMessage, ServerMessageSchema } from "../core/Schemas"
import { LocalServer } from "./LocalServer" import { LocalServer } from "./LocalServer"
@@ -47,6 +47,12 @@ export class SendBoatAttackIntentEvent implements GameEvent {
) { } ) { }
} }
export class SendCreateDestroyerIntentEvent implements GameEvent {
constructor(
public readonly cell: Cell,
) { }
}
export class SendTargetPlayerIntentEvent implements GameEvent { export class SendTargetPlayerIntentEvent implements GameEvent {
constructor( constructor(
public readonly targetID: PlayerID, public readonly targetID: PlayerID,
@@ -115,6 +121,7 @@ export class Transport {
this.eventBus.on(SendDonateIntentEvent, (e) => this.onSendDonateIntent(e)) this.eventBus.on(SendDonateIntentEvent, (e) => this.onSendDonateIntent(e))
this.eventBus.on(SendNukeIntentEvent, (e) => this.onSendNukeIntent(e)) this.eventBus.on(SendNukeIntentEvent, (e) => this.onSendNukeIntent(e))
this.eventBus.on(SendSetTargetTroopRatioEvent, (e) => this.onSendSetTargetTroopRatioEvent(e)) this.eventBus.on(SendSetTargetTroopRatioEvent, (e) => this.onSendSetTargetTroopRatioEvent(e))
this.eventBus.on(SendCreateDestroyerIntentEvent, (e) => this.onCreateDestroyerIntent(e))
} }
connect(onconnect: () => void, onmessage: (message: ServerMessage) => void) { connect(onconnect: () => void, onmessage: (message: ServerMessage) => void) {
@@ -314,6 +321,16 @@ export class Transport {
}) })
} }
private onCreateDestroyerIntent(event: SendCreateDestroyerIntentEvent) {
this.sendIntent({
type: "create_destroyer",
clientID: this.clientID,
player: this.playerID,
x: event.cell.x,
y: event.cell.y,
})
}
private sendIntent(intent: Intent) { private sendIntent(intent: Intent) {
if (this.isLocal || this.socket.readyState === WebSocket.OPEN) { if (this.isLocal || this.socket.readyState === WebSocket.OPEN) {
const msg = ClientIntentMessageSchema.parse({ const msg = ClientIntentMessageSchema.parse({
+31 -12
View File
@@ -1,6 +1,6 @@
import { Colord } from "colord"; import { Colord } from "colord";
import { Theme } from "../../../core/configuration/Config"; import { Theme } from "../../../core/configuration/Config";
import { Unit, BoatEvent, Cell, Game, Tile } from "../../../core/game/Game"; import { Unit, UnitEvent, Cell, Game, Tile, UnitType } from "../../../core/game/Game";
import { bfs, dist } from "../../../core/Util"; import { bfs, dist } from "../../../core/Util";
import { Layer } from "./Layer"; import { Layer } from "./Layer";
import { EventBus } from "../../../core/EventBus"; import { EventBus } from "../../../core/EventBus";
@@ -35,7 +35,7 @@ export class UnitLayer implements Layer {
this.context.putImageData(this.imageData, 0, 0); this.context.putImageData(this.imageData, 0, 0);
this.initImageData() this.initImageData()
this.eventBus.on(BoatEvent, e => this.onBoatEvent(e)) this.eventBus.on(UnitEvent, e => this.onUnitEvent(e))
} }
initImageData() { initImageData() {
@@ -58,28 +58,47 @@ export class UnitLayer implements Layer {
} }
onBoatEvent(event: BoatEvent) { onUnitEvent(event: UnitEvent) {
if (!this.boatToTrail.has(event.boat)) { switch (event.unit.type()) {
this.boatToTrail.set(event.boat, new Set<Tile>()) case UnitType.TransportShip:
this.handleBoatEvent(event)
break
case UnitType.Destroyer:
this.handleDestroyerEvent(event)
break
default:
throw Error(`event for unit ${event.unit.type()} not supported`)
} }
const trail = this.boatToTrail.get(event.boat) }
private handleDestroyerEvent(event: UnitEvent) {
}
private handleBoatEvent(event: UnitEvent) {
if (!this.boatToTrail.has(event.unit)) {
this.boatToTrail.set(event.unit, new Set<Tile>())
}
const trail = this.boatToTrail.get(event.unit)
trail.add(event.oldTile) trail.add(event.oldTile)
bfs(event.oldTile, dist(event.oldTile, 3)).forEach(t => { bfs(event.oldTile, dist(event.oldTile, 3)).forEach(t => {
this.clearCell(t.cell()) this.clearCell(t.cell())
}) })
if (event.boat.isActive()) { if (event.unit.isActive()) {
bfs(event.boat.tile(), dist(event.boat.tile(), 4)).forEach( bfs(event.unit.tile(), dist(event.unit.tile(), 4)).forEach(
t => { t => {
if (trail.has(t)) { if (trail.has(t)) {
this.paintCell(t.cell(), this.theme.territoryColor(event.boat.owner().info()), 150) this.paintCell(t.cell(), this.theme.territoryColor(event.unit.owner().info()), 150)
} }
} }
) )
bfs(event.boat.tile(), dist(event.boat.tile(), 2)).forEach(t => this.paintCell(t.cell(), this.theme.borderColor(event.boat.owner().info()), 255)) bfs(event.unit.tile(), dist(event.unit.tile(), 2))
bfs(event.boat.tile(), dist(event.boat.tile(), 1)).forEach(t => this.paintCell(t.cell(), this.theme.territoryColor(event.boat.owner().info()), 180)) .forEach(t => this.paintCell(t.cell(), this.theme.borderColor(event.unit.owner().info()), 255))
bfs(event.unit.tile(), dist(event.unit.tile(), 1))
.forEach(t => this.paintCell(t.cell(), this.theme.territoryColor(event.unit.owner().info()), 180))
} else { } else {
trail.forEach(t => this.clearCell(t.cell())) trail.forEach(t => this.clearCell(t.cell()))
this.boatToTrail.delete(event.boat) this.boatToTrail.delete(event.unit)
} }
} }
+12 -3
View File
@@ -2,8 +2,9 @@ import { LitElement, html, css } from 'lit';
import { customElement, state } from 'lit/decorators.js'; import { customElement, state } from 'lit/decorators.js';
import { EventBus } from '../../../../core/EventBus'; import { EventBus } from '../../../../core/EventBus';
import { Cell, Game, Item, Items, Player } from '../../../../core/game/Game'; import { Cell, Game, Item, Items, Player } from '../../../../core/game/Game';
import { SendNukeIntentEvent } from '../../../Transport'; import { SendCreateDestroyerIntentEvent, SendNukeIntentEvent } from '../../../Transport';
import nukeIcon from '../../../../../resources/images/NukeIconWhite.svg'; import nukeIcon from '../../../../../resources/images/NukeIconWhite.svg';
import destroyerIcon from '../../../../../resources/images/DestroyerIconWhite.svg';
import goldCoinIcon from '../../../../../resources/images/GoldCoinIcon.svg'; import goldCoinIcon from '../../../../../resources/images/GoldCoinIcon.svg';
import { renderNumber } from '../../Utils'; import { renderNumber } from '../../Utils';
import { ContextMenuEvent } from '../../../InputHandler'; import { ContextMenuEvent } from '../../../InputHandler';
@@ -16,6 +17,7 @@ interface BuildItem {
const buildTable: BuildItem[][] = [ const buildTable: BuildItem[][] = [
[ [
{ item: Items.Nuke, icon: nukeIcon }, { item: Items.Nuke, icon: nukeIcon },
{ item: Items.Destroyer, icon: destroyerIcon },
// { id: 'battleship', name: 'Battleship', icon: '🚢', cost: 500, buildTime: 20 } // { id: 'battleship', name: 'Battleship', icon: '🚢', cost: 500, buildTime: 20 }
] ]
]; ];
@@ -146,8 +148,15 @@ export class BuildMenu extends LitElement {
return this.myPlayer && this.myPlayer.gold() >= item.item.cost; return this.myPlayer && this.myPlayer.gold() >= item.item.cost;
} }
public onBuildSelected: (item: BuildItem) => void = () => { public onBuildSelected = (item: BuildItem) => {
this.eventBus.emit(new SendNukeIntentEvent(this.myPlayer, this.clickedCell, null)) switch (item.item.name) {
case "Nuke":
this.eventBus.emit(new SendNukeIntentEvent(this.myPlayer, this.clickedCell, null))
break
case "Destroyer":
this.eventBus.emit(new SendCreateDestroyerIntentEvent(this.clickedCell))
}
this.hideMenu() this.hideMenu()
}; };
+14 -4
View File
@@ -15,6 +15,7 @@ export type Intent = SpawnIntent
| DonateIntent | DonateIntent
| NukeIntent | NukeIntent
| TargetTroopRatioIntent | TargetTroopRatioIntent
| CreateDestroyerIntent
export type AttackIntent = z.infer<typeof AttackIntentSchema> export type AttackIntent = z.infer<typeof AttackIntentSchema>
export type SpawnIntent = z.infer<typeof SpawnIntentSchema> export type SpawnIntent = z.infer<typeof SpawnIntentSchema>
@@ -26,7 +27,8 @@ export type TargetPlayerIntent = z.infer<typeof TargetPlayerIntentSchema>
export type EmojiIntent = z.infer<typeof EmojiIntentSchema> export type EmojiIntent = z.infer<typeof EmojiIntentSchema>
export type DonateIntent = z.infer<typeof DonateIntentSchema> export type DonateIntent = z.infer<typeof DonateIntentSchema>
export type NukeIntent = z.infer<typeof NukeIntentSchema> export type NukeIntent = z.infer<typeof NukeIntentSchema>
export type TargetTroopRatioIntent = z.infer<typeof TargetTroopRatioSchema> export type TargetTroopRatioIntent = z.infer<typeof TargetTroopRatioIntentSchema>
export type CreateDestroyerIntent = z.infer<typeof CreateDestroyerIntentSchema>
export type Turn = z.infer<typeof TurnSchema> export type Turn = z.infer<typeof TurnSchema>
export type GameConfig = z.infer<typeof GameConfigSchema> export type GameConfig = z.infer<typeof GameConfigSchema>
@@ -67,7 +69,7 @@ const EmojiSchema = z.string().refine(
); );
// Zod schemas // Zod schemas
const BaseIntentSchema = z.object({ const BaseIntentSchema = z.object({
type: z.enum(['attack', 'spawn', 'boat', 'name', 'targetPlayer', 'emoji', 'nuke', 'troop_ratio']), type: z.enum(['attack', 'spawn', 'boat', 'name', 'targetPlayer', 'emoji', 'nuke', 'troop_ratio', 'create_destroyer']),
clientID: z.string(), clientID: z.string(),
}); });
@@ -153,12 +155,19 @@ export const NukeIntentSchema = BaseIntentSchema.extend({
magnitude: z.number().nullable(), magnitude: z.number().nullable(),
}) })
export const TargetTroopRatioSchema = BaseIntentSchema.extend({ export const TargetTroopRatioIntentSchema = BaseIntentSchema.extend({
type: z.literal('troop_ratio'), type: z.literal('troop_ratio'),
player: z.string(), player: z.string(),
ratio: z.number().min(0).max(1), ratio: z.number().min(0).max(1),
}) })
export const CreateDestroyerIntentSchema = BaseIntentSchema.extend({
type: z.literal('create_destroyer'),
player: z.string(),
x: z.number(),
y: z.number(),
})
const IntentSchema = z.union([ const IntentSchema = z.union([
AttackIntentSchema, AttackIntentSchema,
SpawnIntentSchema, SpawnIntentSchema,
@@ -170,7 +179,8 @@ const IntentSchema = z.union([
EmojiIntentSchema, EmojiIntentSchema,
DonateIntentSchema, DonateIntentSchema,
NukeIntentSchema, NukeIntentSchema,
TargetTroopRatioSchema, TargetTroopRatioIntentSchema,
CreateDestroyerIntentSchema,
]); ]);
const TurnSchema = z.object({ const TurnSchema = z.object({
+39
View File
@@ -0,0 +1,39 @@
import { AllPlayers, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, PlayerID, UnitType } from "../game/Game";
export class DestroyerExecution implements Execution {
private _owner: MutablePlayer
private active = true
private destroyer: MutableUnit = null
private mg: MutableGame = null
constructor(
private playerID: PlayerID,
private cell: Cell,
) { }
init(mg: MutableGame, ticks: number): void {
this._owner = mg.player(this.playerID)
this.mg = mg
}
tick(ticks: number): void {
if (this.destroyer == null) {
this.destroyer = this._owner.addUnit(UnitType.Destroyer, 0, this.mg.tile(this.cell))
}
}
owner(): MutablePlayer {
return null
}
isActive(): boolean {
return this.active
}
activeDuringSpawnPhase(): boolean {
return false
}
}
+2 -2
View File
@@ -3,7 +3,7 @@ import { AttackIntent, BoatAttackIntentSchema, GameID, Intent, Turn } from "../S
import { AttackExecution } from "./AttackExecution"; import { AttackExecution } from "./AttackExecution";
import { SpawnExecution } from "./SpawnExecution"; import { SpawnExecution } from "./SpawnExecution";
import { BotSpawner } from "./BotSpawner"; import { BotSpawner } from "./BotSpawner";
import { BoatAttackExecution } from "./BoatAttackExecution"; import { TransportShipExecution } from "./TransportShipExecution";
import { PseudoRandom } from "../PseudoRandom"; import { PseudoRandom } from "../PseudoRandom";
import { FakeHumanExecution } from "./FakeHumanExecution"; import { FakeHumanExecution } from "./FakeHumanExecution";
import Usernames from '../../../resources/Usernames.txt' import Usernames from '../../../resources/Usernames.txt'
@@ -52,7 +52,7 @@ export class Executor {
new Cell(intent.x, intent.y) new Cell(intent.x, intent.y)
) )
} else if (intent.type == "boat") { } else if (intent.type == "boat") {
return new BoatAttackExecution( return new TransportShipExecution(
intent.attackerID, intent.attackerID,
intent.targetID, intent.targetID,
new Cell(intent.x, intent.y), new Cell(intent.x, intent.y),
+2 -2
View File
@@ -2,7 +2,7 @@ import { Cell, Execution, MutableGame, MutablePlayer, Player, PlayerInfo, Player
import { PseudoRandom } from "../PseudoRandom" import { PseudoRandom } from "../PseudoRandom"
import { and, bfs, dist, simpleHash } from "../Util"; import { and, bfs, dist, simpleHash } from "../Util";
import { AttackExecution } from "./AttackExecution"; import { AttackExecution } from "./AttackExecution";
import { BoatAttackExecution } from "./BoatAttackExecution"; import { TransportShipExecution } from "./TransportShipExecution";
import { SpawnExecution } from "./SpawnExecution"; import { SpawnExecution } from "./SpawnExecution";
export class FakeHumanExecution implements Execution { export class FakeHumanExecution implements Execution {
@@ -194,7 +194,7 @@ export class FakeHumanExecution implements Execution {
continue continue
} }
this.mg.addExecution(new BoatAttackExecution( this.mg.addExecution(new TransportShipExecution(
this.player.id(), this.player.id(),
dst.hasOwner() ? dst.owner().id() : null, dst.hasOwner() ? dst.owner().id() : null,
dst.cell(), dst.cell(),
+3 -1
View File
@@ -48,7 +48,9 @@ export class NukeExecution implements Execution {
mp.removeTroops(mp.troops() / mp.numTilesOwned()) mp.removeTroops(mp.troops() / mp.numTilesOwned())
} }
} }
this.mg.boats().filter(b => euclideanDist(this.cell, b.tile().cell()) < this.magnitude + 50).forEach(b => b.delete()) this.mg.units()
.filter(b => euclideanDist(this.cell, b.tile().cell()) < this.magnitude + 50)
.forEach(b => b.delete())
this.active = false this.active = false
} }
@@ -4,7 +4,7 @@ import { and, bfs, manhattanDistWrapped, sourceDstOceanShore } from "../Util";
import { AttackExecution } from "./AttackExecution"; import { AttackExecution } from "./AttackExecution";
import { DisplayMessageEvent, MessageType } from "../../client/graphics/layers/EventsDisplay"; import { DisplayMessageEvent, MessageType } from "../../client/graphics/layers/EventsDisplay";
export class BoatAttackExecution implements Execution { export class TransportShipExecution implements Execution {
private lastMove: number private lastMove: number
@@ -86,7 +86,7 @@ export class BoatAttackExecution implements Execution {
this.aStarPre.compute(5) this.aStarPre.compute(5)
this.path = this.aStarPre.reconstructPath() this.path = this.aStarPre.reconstructPath()
if (this.path != null) { if (this.path != null) {
this.boat = this.attacker.addBoat(this.troops, this.src, this.target) this.boat = this.attacker.addUnit(UnitType.TransportShip, this.troops, this.src)
} else { } else {
console.log('got null path') console.log('got null path')
this.active = false this.active = false
@@ -126,7 +126,9 @@ export class BoatAttackExecution implements Execution {
this.target.addTroops(this.troops) this.target.addTroops(this.troops)
} else { } else {
this.attacker.conquer(this.dst) this.attacker.conquer(this.dst)
this.mg.addExecution(new AttackExecution(this.troops, this.attacker.id(), this.targetID, this.dst.cell(), null, false)) this.mg.addExecution(
new AttackExecution(this.troops, this.attacker.id(), this.targetID, this.dst.cell(), null, false)
)
} }
this.boat.delete() this.boat.delete()
this.active = false this.active = false
+9 -6
View File
@@ -26,7 +26,8 @@ export enum GameMap {
} }
export enum UnitType { export enum UnitType {
TransportShip TransportShip,
Destroyer
} }
export class Item { export class Item {
@@ -35,6 +36,7 @@ export class Item {
export const Items = { export const Items = {
Nuke: new Item("Nuke", 1_000_000), Nuke: new Item("Nuke", 1_000_000),
Destroyer: new Item("Destroyer", 100_000)
} as const; } as const;
export class Nation { export class Nation {
@@ -226,7 +228,6 @@ export interface MutablePlayer extends Player {
allianceWith(other: Player): MutableAlliance | null allianceWith(other: Player): MutableAlliance | null
breakAlliance(alliance: Alliance): void breakAlliance(alliance: Alliance): void
createAllianceRequest(recipient: Player): MutableAllianceRequest createAllianceRequest(recipient: Player): MutableAllianceRequest
addBoat(troops: number, tile: Tile, target: Player | TerraNullius): MutableUnit
target(other: Player): void target(other: Player): void
targets(): MutablePlayer[] targets(): MutablePlayer[]
transitiveTargets(): MutablePlayer[] transitiveTargets(): MutablePlayer[]
@@ -242,6 +243,8 @@ export interface MutablePlayer extends Player {
setTroops(troops: number): void setTroops(troops: number): void
addTroops(troops: number): void addTroops(troops: number): void
removeTroops(troops: number): number removeTroops(troops: number): number
addUnit(type: UnitType, troops: number, tile: Tile): MutableUnit
} }
export interface Game { export interface Game {
@@ -266,7 +269,7 @@ export interface Game {
nations(): Nation[] nations(): Nation[]
config(): Config config(): Config
displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void
boats(): Unit[] units(...types: UnitType[]): Unit[]
} }
export interface MutableGame extends Game { export interface MutableGame extends Game {
@@ -275,7 +278,7 @@ export interface MutableGame extends Game {
players(): MutablePlayer[] players(): MutablePlayer[]
addPlayer(playerInfo: PlayerInfo, manpower: number): MutablePlayer addPlayer(playerInfo: PlayerInfo, manpower: number): MutablePlayer
executions(): Execution[] executions(): Execution[]
boats(): MutableUnit[] units(...types: UnitType[]): MutableUnit[]
} }
export class TileEvent implements GameEvent { export class TileEvent implements GameEvent {
@@ -286,8 +289,8 @@ export class PlayerEvent implements GameEvent {
constructor(public readonly player: Player) { } constructor(public readonly player: Player) { }
} }
export class BoatEvent implements GameEvent { export class UnitEvent implements GameEvent {
constructor(public readonly boat: Unit, public oldTile: Tile) { } constructor(public readonly unit: Unit, public oldTile: Tile) { }
} }
export class AllianceRequestEvent implements GameEvent { export class AllianceRequestEvent implements GameEvent {
+3 -3
View File
@@ -1,7 +1,7 @@
import { info } from "console"; import { info } from "console";
import { Config } from "../configuration/Config"; import { Config } from "../configuration/Config";
import { EventBus } from "../EventBus"; import { EventBus } from "../EventBus";
import { Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Unit, BoatEvent as UnitEvent, PlayerType, MutableAllianceRequest, AllianceRequestReplyEvent, AllianceRequestEvent, BrokeAllianceEvent, MutableAlliance, Alliance, AllianceExpiredEvent, Nation } from "./Game"; import { Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Unit, UnitEvent as UnitEvent, PlayerType, MutableAllianceRequest, AllianceRequestReplyEvent, AllianceRequestEvent, BrokeAllianceEvent, MutableAlliance, Alliance, AllianceExpiredEvent, Nation, UnitType } from "./Game";
import { TerrainMap } from "./TerrainMapLoader"; import { TerrainMap } from "./TerrainMapLoader";
import { PlayerImpl } from "./PlayerImpl"; import { PlayerImpl } from "./PlayerImpl";
import { TerraNulliusImpl } from "./TerraNulliusImpl"; import { TerraNulliusImpl } from "./TerraNulliusImpl";
@@ -58,8 +58,8 @@ export class GameImpl implements MutableGame {
n.strength n.strength
)) ))
} }
boats(): UnitImpl[] { units(...types: UnitType[]): UnitImpl[] {
return Array.from(this._players.values()).flatMap(p => p._units) return Array.from(this._players.values()).flatMap(p => p.units(...types))
} }
nations(): Nation[] { nations(): Nation[] {
return this.nations_ return this.nations_
+2 -2
View File
@@ -73,8 +73,8 @@ export class PlayerImpl implements MutablePlayer {
} }
addBoat(troops: number, tile: Tile): UnitImpl { addUnit(type: UnitType, troops: number, tile: Tile): UnitImpl {
const b = new UnitImpl(UnitType.TransportShip, this.gs, tile, troops, this); const b = new UnitImpl(type, this.gs, tile, troops, this);
this._units.push(b); this._units.push(b);
this.gs.fireUnitUpdateEvent(b, b.tile()); this.gs.fireUnitUpdateEvent(b, b.tile());
return b; return b;