mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-28 01:14:15 +00:00
Merge branch 'main' into defenseposture
This commit is contained in:
@@ -12,6 +12,7 @@ import { createGameRecord } from "../core/Util";
|
||||
import { ServerConfig } from "../core/configuration/Config";
|
||||
import { getConfig } from "../core/configuration/ConfigLoader";
|
||||
import { Team, UnitType } from "../core/game/Game";
|
||||
import { TileRef } from "../core/game/GameMap";
|
||||
import {
|
||||
ErrorUpdate,
|
||||
GameUpdateType,
|
||||
@@ -28,6 +29,7 @@ import { endGame, startGame, startTime } from "./LocalPersistantStats";
|
||||
import { getPersistentIDFromCookie } from "./Main";
|
||||
import {
|
||||
SendAttackIntentEvent,
|
||||
SendBoatAttackIntentEvent,
|
||||
SendHashEvent,
|
||||
SendSpawnIntentEvent,
|
||||
Transport,
|
||||
@@ -359,6 +361,18 @@ export class ClientGameRunner {
|
||||
this.myPlayer.troops() * this.renderer.uiState.attackRatio,
|
||||
),
|
||||
);
|
||||
} else if (
|
||||
actions.canBoat !== false &&
|
||||
this.shouldBoat(tile, actions.canBoat) &&
|
||||
this.gameView.isLand(tile)
|
||||
) {
|
||||
this.eventBus.emit(
|
||||
new SendBoatAttackIntentEvent(
|
||||
this.gameView.owner(tile).id(),
|
||||
cell,
|
||||
this.myPlayer.troops() * this.renderer.uiState.attackRatio,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const owner = this.gameView.owner(tile);
|
||||
@@ -370,6 +384,18 @@ export class ClientGameRunner {
|
||||
});
|
||||
}
|
||||
|
||||
private shouldBoat(tile: TileRef, src: TileRef) {
|
||||
// TODO: Global enable flag
|
||||
// TODO: Global limit autoboat to nearby shore flag
|
||||
// if (!enableAutoBoat) return false;
|
||||
// if (!limitAutoBoatNear) return true;
|
||||
const distanceSquared = this.gameView.euclideanDistSquared(tile, src);
|
||||
const limit = 100;
|
||||
const limitSquared = limit * limit;
|
||||
if (distanceSquared > limitSquared) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private onMouseMove(event: MouseMoveEvent) {
|
||||
this.lastMousePosition = { x: event.x, y: event.y };
|
||||
this.checkTileUnderCursor();
|
||||
|
||||
@@ -23,6 +23,7 @@ export const MapDescription: Record<keyof typeof GameMapType, string> = {
|
||||
Japan: "Japan",
|
||||
BetweenTwoSeas: "Between Two Seas",
|
||||
KnownWorld: "Known World",
|
||||
FaroeIslands: "Faroe Islands",
|
||||
};
|
||||
|
||||
@customElement("map-display")
|
||||
|
||||
@@ -145,8 +145,14 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
this.eventBus.on(MouseUpEvent, (e: MouseEvent) => this.hide());
|
||||
}
|
||||
|
||||
tick() {
|
||||
this.requestUpdate();
|
||||
async tick() {
|
||||
if (this.isVisible && this.tile) {
|
||||
const myPlayer = this.g.myPlayer();
|
||||
if (myPlayer !== null && myPlayer.isAlive()) {
|
||||
this.actions = await myPlayer.actions(this.tile);
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getTotalNukesSent(otherId: PlayerID): number {
|
||||
|
||||
@@ -8,7 +8,7 @@ import swordIcon from "../../../../resources/images/SwordIconWhite.svg";
|
||||
import traitorIcon from "../../../../resources/images/TraitorIconWhite.svg";
|
||||
import { consolex } from "../../../core/Consolex";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Cell, PlayerActions } from "../../../core/game/Game";
|
||||
import { Cell, PlayerActions, TerraNullius } from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { ClientID } from "../../../core/Schemas";
|
||||
@@ -44,6 +44,7 @@ export class RadialMenu implements Layer {
|
||||
private clickedCell: Cell | null = null;
|
||||
private lastClosed: number = 0;
|
||||
|
||||
private originalTileOwner: PlayerView | TerraNullius;
|
||||
private menuElement: d3.Selection<HTMLDivElement, unknown, null, undefined>;
|
||||
private isVisible: boolean = false;
|
||||
private readonly menuItems = new Map([
|
||||
@@ -267,8 +268,26 @@ export class RadialMenu implements Layer {
|
||||
.style("pointer-events", "none");
|
||||
}
|
||||
|
||||
tick() {
|
||||
// Update logic if needed
|
||||
async tick() {
|
||||
// Only update when menu is visible
|
||||
if (!this.isVisible || this.clickedCell === null) return;
|
||||
const myPlayer = this.g.myPlayer();
|
||||
if (myPlayer === null || !myPlayer.isAlive()) return;
|
||||
const tile = this.g.ref(this.clickedCell.x, this.clickedCell.y);
|
||||
if (this.originalTileOwner.isPlayer()) {
|
||||
if (this.g.owner(tile) != this.originalTileOwner) {
|
||||
this.closeMenu();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (this.g.owner(tile).isPlayer() || this.g.owner(tile) == myPlayer) {
|
||||
this.closeMenu();
|
||||
return;
|
||||
}
|
||||
}
|
||||
const actions = await myPlayer.actions(tile);
|
||||
this.disableAllButtons();
|
||||
this.handlePlayerActions(myPlayer, actions, tile);
|
||||
}
|
||||
|
||||
renderLayer(context: CanvasRenderingContext2D) {
|
||||
@@ -291,12 +310,7 @@ export class RadialMenu implements Layer {
|
||||
} else {
|
||||
this.showRadialMenu(event.x, event.y);
|
||||
}
|
||||
this.enableCenterButton(false);
|
||||
for (const item of this.menuItems.values()) {
|
||||
item.disabled = true;
|
||||
this.updateMenuItemState(item);
|
||||
}
|
||||
|
||||
this.disableAllButtons();
|
||||
this.clickedCell = this.transformHandler.screenToWorldCoordinates(
|
||||
event.x,
|
||||
event.y,
|
||||
@@ -305,7 +319,7 @@ export class RadialMenu implements Layer {
|
||||
return;
|
||||
}
|
||||
const tile = this.g.ref(this.clickedCell.x, this.clickedCell.y);
|
||||
|
||||
this.originalTileOwner = this.g.owner(tile);
|
||||
if (this.g.inSpawnPhase()) {
|
||||
if (this.g.isLand(tile) && !this.g.hasOwner(tile)) {
|
||||
this.enableCenterButton(true);
|
||||
@@ -313,10 +327,8 @@ export class RadialMenu implements Layer {
|
||||
return;
|
||||
}
|
||||
|
||||
const myPlayer = this.g
|
||||
.playerViews()
|
||||
.find((p) => p.clientID() == this.clientID);
|
||||
if (!myPlayer) {
|
||||
const myPlayer = this.g.myPlayer();
|
||||
if (myPlayer === null) {
|
||||
consolex.warn("my player not found");
|
||||
return;
|
||||
}
|
||||
@@ -430,6 +442,14 @@ export class RadialMenu implements Layer {
|
||||
this.hideRadialMenu();
|
||||
}
|
||||
|
||||
private disableAllButtons() {
|
||||
this.enableCenterButton(false);
|
||||
for (const item of this.menuItems.values()) {
|
||||
item.disabled = true;
|
||||
this.updateMenuItemState(item);
|
||||
}
|
||||
}
|
||||
|
||||
private activateMenuElement(
|
||||
slot: Slot,
|
||||
color: string,
|
||||
|
||||
@@ -5,6 +5,7 @@ import betweenTwoSeas from "../../../resources/maps/BetweenTwoSeasThumb.webp";
|
||||
import blackSea from "../../../resources/maps/BlackSeaThumb.webp";
|
||||
import britannia from "../../../resources/maps/BritanniaThumb.webp";
|
||||
import europe from "../../../resources/maps/EuropeThumb.webp";
|
||||
import faroeislands from "../../../resources/maps/FaroeIslandsThumb.webp";
|
||||
import gatewayToTheAtlantic from "../../../resources/maps/GatewayToTheAtlanticThumb.webp";
|
||||
import iceland from "../../../resources/maps/IcelandThumb.webp";
|
||||
import japan from "../../../resources/maps/JapanThumb.webp";
|
||||
@@ -57,6 +58,8 @@ export function getMapsImage(map: GameMapType): string {
|
||||
return betweenTwoSeas;
|
||||
case GameMapType.KnownWorld:
|
||||
return knownworld;
|
||||
case GameMapType.FaroeIslands:
|
||||
return faroeislands;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export abstract class DefaultServerConfig implements ServerConfig {
|
||||
return process.env.GIT_COMMIT;
|
||||
}
|
||||
r2Endpoint(): string {
|
||||
return process.env.R2_ENDPOINT;
|
||||
return `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`;
|
||||
}
|
||||
r2AccessKey(): string {
|
||||
return process.env.R2_ACCESS_KEY;
|
||||
@@ -89,6 +89,7 @@ export abstract class DefaultServerConfig implements ServerConfig {
|
||||
GameMapType.Mars,
|
||||
GameMapType.Oceania,
|
||||
GameMapType.Japan, // Japan at this level because its 2/3 water
|
||||
GameMapType.FaroeIslands,
|
||||
].includes(map)
|
||||
) {
|
||||
return Math.random() < 0.2 ? 70 : 40;
|
||||
|
||||
@@ -67,6 +67,7 @@ export enum GameMapType {
|
||||
Japan = "Japan",
|
||||
BetweenTwoSeas = "Between Two Seas",
|
||||
KnownWorld = "Known World",
|
||||
FaroeIslands = "FaroeIslands",
|
||||
}
|
||||
|
||||
export enum GameType {
|
||||
@@ -416,7 +417,7 @@ export interface Player {
|
||||
// Misc
|
||||
toUpdate(): PlayerUpdate;
|
||||
playerProfile(): PlayerProfile;
|
||||
canBoat(tile: TileRef): boolean;
|
||||
canBoat(tile: TileRef): TileRef | false;
|
||||
tradingPorts(port: Unit): Unit[];
|
||||
}
|
||||
|
||||
@@ -474,7 +475,7 @@ export interface Game extends GameMap {
|
||||
}
|
||||
|
||||
export interface PlayerActions {
|
||||
canBoat: boolean;
|
||||
canBoat: TileRef | false;
|
||||
canAttack: boolean;
|
||||
buildableUnits: BuildableUnit[];
|
||||
canSendEmojiAllPlayers: boolean;
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
Attack,
|
||||
Cell,
|
||||
EmojiMessage,
|
||||
GameMode,
|
||||
Gold,
|
||||
MessageType,
|
||||
MutableAlliance,
|
||||
@@ -518,6 +519,13 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
canDonate(recipient: Player): boolean {
|
||||
if (
|
||||
recipient.type() == PlayerType.Human &&
|
||||
this.mg.config().gameConfig().gameMode == GameMode.FFA
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.isFriendly(recipient)) {
|
||||
return false;
|
||||
}
|
||||
@@ -543,7 +551,7 @@ export class PlayerImpl implements Player {
|
||||
this.id(),
|
||||
);
|
||||
this.mg.displayMessage(
|
||||
`Recieved ${renderTroops(troops)} troops from ${this.name()}`,
|
||||
`Received ${renderTroops(troops)} troops from ${this.name()}`,
|
||||
MessageType.SUCCESS,
|
||||
recipient.id(),
|
||||
);
|
||||
@@ -557,7 +565,7 @@ export class PlayerImpl implements Player {
|
||||
this.id(),
|
||||
);
|
||||
this.mg.displayMessage(
|
||||
`Recieved ${renderNumber(gold)} gold from ${this.name()}`,
|
||||
`Received ${renderNumber(gold)} gold from ${this.name()}`,
|
||||
MessageType.SUCCESS,
|
||||
recipient.id(),
|
||||
);
|
||||
@@ -886,7 +894,7 @@ export class PlayerImpl implements Player {
|
||||
return rel;
|
||||
}
|
||||
|
||||
public canBoat(tile: TileRef): boolean {
|
||||
public canBoat(tile: TileRef): TileRef | false {
|
||||
if (
|
||||
this.units(UnitType.TransportShip).length >=
|
||||
this.mg.config().boatMaxNumber()
|
||||
@@ -929,7 +937,7 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
if (myPlayerBordersOcean && otherPlayerBordersOcean) {
|
||||
return this.canBuild(UnitType.TransportShip, dst) != false;
|
||||
return this.canBuild(UnitType.TransportShip, dst);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -952,7 +960,7 @@ export class PlayerImpl implements Player {
|
||||
|
||||
for (const t of sorted) {
|
||||
if (this.mg.owner(t) == this) {
|
||||
return this.canBuild(UnitType.TransportShip, dst) != false;
|
||||
return this.canBuild(UnitType.TransportShip, dst);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -41,6 +41,7 @@ const MAP_FILE_NAMES: Record<GameMapType, string> = {
|
||||
[GameMapType.Japan]: "Japan",
|
||||
[GameMapType.BetweenTwoSeas]: "BetweenTwoSeas",
|
||||
[GameMapType.KnownWorld]: "KnownWorld",
|
||||
[GameMapType.FaroeIslands]: "FaroeIslands",
|
||||
};
|
||||
|
||||
class GameMapLoader {
|
||||
|
||||
@@ -22,6 +22,7 @@ const maps = [
|
||||
"BetweenTwoSeas",
|
||||
"Japan",
|
||||
"KnownWorld",
|
||||
"FaroeIslands",
|
||||
];
|
||||
|
||||
const removeSmall = true;
|
||||
|
||||
@@ -101,6 +101,7 @@ export class MapPlaylist {
|
||||
BetweenTwoSeas: 3,
|
||||
Japan: 3,
|
||||
BlackSea: 1,
|
||||
FaroeIslands: 2,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user