Skip to content

Commit

Permalink
Merge pull request #895 from virtualcommons/solo
Browse files Browse the repository at this point in the history
feat: add solo mode
  • Loading branch information
alee authored Oct 18, 2023
2 parents 1b962ab + e2327b4 commit 4ed1283
Show file tree
Hide file tree
Showing 73 changed files with 3,216 additions and 63 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/check-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Check Code Style/Lint

on:
push:
branches: [main]
branches: [ main ]
pull_request:
branches: [main]
branches: [ main ]

jobs:
client:
Expand Down
16 changes: 12 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ BUILD_ID=$(shell git describe --tags --abbrev=1)

.PHONY: build
build: docker-compose.yml
docker compose pull db
docker compose pull db redis
docker compose build --pull

.PHONY: browser
Expand Down Expand Up @@ -113,24 +113,32 @@ docker-compose.yml: base.yml $(ENVIR).yml config.mk $(DB_DATA_PATH) $(DATA_DUMP_

.PHONY: test-setup
test-setup: docker-compose.yml
docker compose run --rm server bash -c "dropdb --if-exists -h db -U ${DB_USER} ${TEST_DB_NAME} && createdb -h db -U ${DB_USER} ${TEST_DB_NAME} && yarn typeorm schema:sync -c test"
docker compose run --rm server bash -c "dropdb --if-exists -h db -U ${DB_USER} ${TEST_DB_NAME} && createdb -h db -U ${DB_USER} ${TEST_DB_NAME} && yarn typeorm schema:sync -c test && yarn load-fixtures ./fixtures/sologame -cn test"

.PHONY: test
test: test-setup
docker compose run --rm client yarn test:unit
docker compose run --rm server yarn test

.PHONY: test-server
test-server: test-setup
docker compose run --rm server yarn test $(tests)

.PHONY: deploy
deploy: build
docker compose pull db redis
docker compose up -d


.PHONY: buildprod
buildprod: docker-compose.yml
docker compose run --rm client yarn build
docker compose run --rm server yarn build

.PHONY: docker-clean
docker-clean:
docker volume prune -a -f
docker compose down
docker compose build --pull --no-cache

.PHONY: clean
clean:
rm -f server/.env # any other generated resources? SHARED_CONFIG_PATH?
33 changes: 33 additions & 0 deletions client/src/api/sologame/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Room } from "colyseus.js";
import { EventContinue, Invest, SoloGameRequest } from "@port-of-mars/shared/sologame";

export class SoloGameRequestAPI {
room!: Room;

connect(room: Room) {
this.room = room;
}

public leave() {
if (this.room) {
this.room.leave();
}
}

public send(req: SoloGameRequest) {
this.room.send(req.kind, req);
}

public eventContinue() {
const msg: EventContinue = { kind: "event-continue" };
this.send(msg);
}

public invest(systemHealthInvestment: number) {
const msg: Invest = {
kind: "invest",
systemHealthInvestment,
};
this.send(msg);
}
}
67 changes: 67 additions & 0 deletions client/src/api/sologame/response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Room } from "colyseus.js";
import { DataChange, Schema } from "@colyseus/schema";
import { SetHiddenParams } from "@port-of-mars/shared/sologame";

type Schemify<T> = T & Schema;

function deschemify<T>(s: Schemify<T>): T {
return s.toJSON() as T;
}

export function applySoloGameServerResponses(room: Room, component: any) {
// const store = component.$tstore;
// const router = component.$router;

room.onError((code: number, message?: string) => {
console.log(`Error ${code} occurred in room: ${message} `);
alert("sorry, we encountered an error, please try refreshing the page or contact us");
});

room.onLeave((code: number) => {
console.log(`client left the room: ${code}`);
});

function applyChanges(target: any, changes: DataChange[], fields: string[]) {
changes.forEach(change => {
if (fields.includes(change.field)) {
target[change.field] = change.value;
}
});
}

room.state.player.onChange = (changes: DataChange[]) => {
applyChanges(component.state.player, changes, ["points", "resources"]);
};

room.state.treatmentParams.onChange = (changes: DataChange[]) => {
applyChanges(component.state.treatmentParams, changes, [
"isNumberOfRoundsKnown",
"isEventDeckKnown",
"thresholdInformation",
]);
};

room.state.onChange = (changes: DataChange[]) => {
applyChanges(component.state, changes, [
"round",
"timeRemaining",
"systemHealth",
"activeCardId",
"canInvest",
"isRoundTransitioning",
"status",
]);
};

room.state.visibleEventCards.onAdd = (eventCard: any, key: number) => {
component.state.visibleEventCards.push(deschemify(eventCard));
};

room.state.visibleEventCards.onRemove = (eventCard: any, key: number) => {
component.state.visibleEventCards.shift();
};

room.onMessage("set-hidden-params", (msg: SetHiddenParams) => {
component.state = { ...component.state, ...msg.data };
});
}
32 changes: 31 additions & 1 deletion client/src/api/stats/request.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { url } from "@port-of-mars/client/util";
import { LeaderboardData, PlayerStatItem } from "@port-of-mars/shared/types";
import {
LeaderboardData,
PlayerStatItem,
SoloHighScoreData,
SoloPlayerStatItem,
} from "@port-of-mars/shared/types";
import { TStore } from "@port-of-mars/client/plugins/tstore";
import { AjaxRequest } from "@port-of-mars/client/plugins/ajax";

Expand Down Expand Up @@ -30,4 +35,29 @@ export class StatsAPI {
throw e;
}
}

async getSoloHighScoreData(limit?: number): Promise<SoloHighScoreData> {
try {
const params = limit ? `?limit=${limit}` : "";
return await this.ajax.get(url(`/stats/solo/highscores${params}`), ({ data }) => {
return data;
});
} catch (e) {
console.log("Unable to retrieve solo highscores data");
console.log(e);
throw e;
}
}

async getSoloPlayerHistory(): Promise<Array<SoloPlayerStatItem>> {
try {
return await this.ajax.get(url("/stats/solo/history"), ({ data }) => {
return data;
});
} catch (e) {
console.log("Unable to retrieve player solo-game history");
console.log(e);
throw e;
}
}
}
Binary file not shown.
Binary file not shown.
13 changes: 9 additions & 4 deletions client/src/components/global/Navbar.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<b-navbar tag="header" toggleable="md" type="dark" variant="dark" class="w-100" fixed="top">
<b-navbar tag="header" toggleable="lg" type="dark" variant="dark" class="w-100" fixed="top">
<b-navbar-brand :to="home">
<b-img
id="logo"
Expand Down Expand Up @@ -36,9 +36,12 @@
<b-nav-item class="mx-2" :to="leaderboard" exact-active-class="active" title="Leaderboard"
>Leaderboard</b-nav-item
>
<b-nav-item class="mx-2" :to="lobby" exact-active-class="active" title="Game Lobby"
>Play</b-nav-item
>
<b-nav-item class="mx-2" :to="solo" exact-active-class="active" title="Solo Minigame">
Solo Mode
</b-nav-item>
<b-nav-item class="mx-2" :to="lobby" exact-active-class="active" title="Game Lobby">
Play Port of Mars
</b-nav-item>
</b-navbar-nav>
<b-navbar-nav class="ml-auto">
<b-nav-item class="mx-2" target="_blank" :href="constants.DISCORD_URL" title="Discord"
Expand Down Expand Up @@ -79,6 +82,7 @@ import {
CONSENT_PAGE,
MANUAL_PAGE,
GAME_PAGE,
SOLO_GAME_PAGE,
LOBBY_PAGE,
PLAYER_HISTORY_PAGE,
LEADERBOARD_PAGE,
Expand Down Expand Up @@ -108,6 +112,7 @@ export default class Header extends Vue {
login = { name: LOGIN_PAGE };
manual = { name: MANUAL_PAGE };
game = { name: GAME_PAGE };
solo = { name: SOLO_GAME_PAGE };
leaderboard = { name: LEADERBOARD_PAGE };
history = { name: PLAYER_HISTORY_PAGE };
lobby = { name: LOBBY_PAGE };
Expand Down
38 changes: 38 additions & 0 deletions client/src/components/sologame/Clock.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<template>
<div class="d-flex py-2 px-3 vfd-container">
<VFDNumberDisplay :digits="2" :value="minutesRemaining" variant="red" :size="3" />
<p style="font-size: 3rem" class="vfd-text-glow vfd-red">:</p>
<VFDNumberDisplay
:digits="2"
:value="secondsRemaining"
:padZeros="true"
variant="red"
:size="3"
/>
</div>
</template>

<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
import VFDNumberDisplay from "@port-of-mars/client/components/sologame/VFDNumberDisplay.vue";
@Component({
components: {
VFDNumberDisplay,
},
})
export default class Clock extends Vue {
@Prop({ default: 0 }) timeRemaining!: number;
@Prop({ default: 3 }) size!: number; // in rem
get secondsRemaining() {
return this.timeRemaining % 60;
}
get minutesRemaining() {
return Math.floor(this.timeRemaining / 60);
}
}
</script>

<style lang="scss"></style>
Loading

0 comments on commit 4ed1283

Please sign in to comment.