Skip to content

Commit

Permalink
Merge pull request #951 from sgfost/sologame-prolific
Browse files Browse the repository at this point in the history
additional solo game configuration and integration with prolific
  • Loading branch information
sgfost authored Nov 22, 2024
2 parents 3374a88 + 21f6404 commit 7dae91a
Show file tree
Hide file tree
Showing 42 changed files with 2,334 additions and 223 deletions.
36 changes: 19 additions & 17 deletions client/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<b-container class="h-100 p-0 m-0 bg" fluid>
<b-row no-gutters class="h-100 w-100">
<Navbar v-if="!isGamePage"></Navbar>
<Navbar v-if="showNav"></Navbar>
<router-view :class="bodyClass" :key="topLevelPath"></router-view>
</b-row>
</b-container>
Expand All @@ -19,8 +19,8 @@ import {
HOME_PAGE,
ABOUT_PAGE,
PRIVACY_PAGE,
PROLIFIC_STUDY_PAGE,
} from "@port-of-mars/shared/routes";
import _ from "lodash";
Vue.use(BootstrapVue);
@Component({
Expand All @@ -36,29 +36,31 @@ export default class App extends Vue {
home = { name: HOME_PAGE };
about = { name: ABOUT_PAGE };
privacy = { name: PRIVACY_PAGE };
study = { name: PROLIFIC_STUDY_PAGE };
get topLevelPath() {
return this.$route.path.split("/")[1];
}
get isGamePage() {
if (_.isNil(this.$route.name)) {
return false;
} else {
return this.game.name === this.$route.name;
get showNav() {
switch (this.$route.name) {
case this.game.name:
case this.study.name:
return false;
default:
return true;
}
}
get isScrollable() {
if (_.isNil(this.$route.name)) {
return false;
} else {
return (
this.manual.name === this.$route.name ||
this.home.name === this.$route.name ||
this.about.name === this.$route.name ||
this.privacy.name === this.$route.name
);
switch (this.$route.name) {
case this.manual.name:
case this.privacy.name:
case this.about.name:
case this.home.name:
return true;
default:
return false;
}
}
Expand All @@ -68,7 +70,7 @@ export default class App extends Vue {
{ "d-flex": !this.isScrollable },
{ "flex-grow-1": !this.isScrollable },
{ "h-auto": this.isScrollable },
{ "body-content": !this.isGamePage },
{ "body-content": this.showNav },
];
}
}
Expand Down
9 changes: 8 additions & 1 deletion client/src/api/sologame/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,26 @@ import { Room } from "colyseus.js";
import { EventContinue, Invest, SoloGameRequest } from "@port-of-mars/shared/sologame";

export class SoloGameRequestAPI {
room!: Room;
room: Room | null = null;

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

reset() {
this.room = null;
}

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

public send(req: SoloGameRequest) {
if (!this.room) {
throw new Error("room not connected");
}
this.room.send(req.kind, req);
}

Expand Down
26 changes: 25 additions & 1 deletion client/src/api/sologame/response.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Room } from "colyseus.js";
import { DataChange, Schema } from "@colyseus/schema";
import { SetHiddenParams } from "@port-of-mars/shared/sologame";
import { SetHiddenParams, SoloGameClientState } from "@port-of-mars/shared/sologame";

type Schemify<T> = T & Schema;

Expand Down Expand Up @@ -38,11 +38,13 @@ export function applySoloGameServerResponses(room: Room, component: any) {
"isNumberOfRoundsKnown",
"isEventDeckKnown",
"thresholdInformation",
"isLowResSystemHealth",
]);
};

room.state.onChange = (changes: DataChange[]) => {
applyChanges(component.state, changes, [
"type",
"round",
"timeRemaining",
"systemHealth",
Expand All @@ -65,3 +67,25 @@ export function applySoloGameServerResponses(room: Room, component: any) {
component.state = { ...component.state, ...msg.data };
});
}

export const DEFAULT_STATE: SoloGameClientState = {
type: "freeplay",
status: "incomplete",
timeRemaining: 0,
systemHealth: 0,
round: 0,
treatmentParams: {
isNumberOfRoundsKnown: false,
isEventDeckKnown: false,
thresholdInformation: "unknown",
isLowResSystemHealth: false,
},
player: {
resources: 0,
points: 0,
},
visibleEventCards: [],
activeCardId: -1,
canInvest: false,
isRoundTransitioning: false,
};
46 changes: 46 additions & 0 deletions client/src/api/study/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { url } from "@port-of-mars/client/util";
import { ProlificStudyData, ProlificParticipantStatus } from "@port-of-mars/shared/types";
import { TStore } from "@port-of-mars/client/plugins/tstore";
import { AjaxRequest } from "@port-of-mars/client/plugins/ajax";

export class StudyAPI {
constructor(public store: TStore, public ajax: AjaxRequest) {}

async getProlificParticipantStatus(): Promise<ProlificParticipantStatus> {
return this.ajax.get(url("/study/prolific/status"), ({ data }) => {
return data;
});
}

async completeProlificStudy(): Promise<string> {
return this.ajax.get(url("/study/prolific/complete"), ({ data }) => {
return data;
});
}

async getAllProlificStudies(): Promise<ProlificStudyData[]> {
return this.ajax.get(url("/study/prolific/studies"), ({ data }) => {
return data;
});
}

async addProlificStudy(study: ProlificStudyData): Promise<ProlificStudyData> {
return this.ajax.post(
url("/study/prolific/add"),
({ data }) => {
return data;
},
study
);
}

async updateProlificStudy(study: ProlificStudyData): Promise<ProlificStudyData> {
return this.ajax.post(
url(`/study/prolific/update/?studyId=${study.studyId}`),
({ data }) => {
return data;
},
study
);
}
}
52 changes: 48 additions & 4 deletions client/src/components/sologame/Dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,22 @@
/>
<div class="d-flex flex-row flex-grow-1 overflow-hidden">
<div class="d-flex flex-column flex-grow-1 overflow-hidden">
<div class="d-flex flex-shrink-1 m-2 mt-3">
<div
v-if="state.treatmentParams.isLowResSystemHealth"
class="d-flex flex-shrink-1 m-2 mt-3"
>
<SegmentedBar
:min="0"
:max="5"
:delta="0"
v-model="lowResSystemHealth"
:customTextDisplay="lowResSystemHealthText"
label="System Health"
class="flex-grow-1"
variant="green"
/>
</div>
<div v-else class="d-flex flex-shrink-1 m-2 mt-3">
<SegmentedBar
:min="0"
:max="25"
Expand All @@ -19,8 +34,10 @@
variant="green"
/>
</div>
<div class="d-flex flex-md-row flex-column flex-grow-1 overflow-hidden mh-50">
<div class="cell-grow mw-35">
<div
class="d-flex flex-md-row flex-column flex-grow-1 overflow-hidden mh-50 justify-content-center"
>
<div v-if="!isProlificBaselineGame" class="cell-grow mw-35">
<div>
<ThresholdInfo
v-if="state.treatmentParams.thresholdInformation !== 'unknown'"
Expand Down Expand Up @@ -100,7 +117,11 @@
</div>
</div>
</div>
<div class="cell-shrink mw-25" style="min-width: 25%; padding: 0.5em">
<div
v-if="!isProlificBaselineGame"
class="cell-shrink mw-25"
style="min-width: 25%; padding: 0.5em"
>
<h4>Events</h4>
<Deck :events="state.visibleEventCards" @active-card-changed="handleActiveCardChange" />
</div>
Expand Down Expand Up @@ -148,6 +169,29 @@ export default class Dashboard extends Vue {
return this.state.visibleEventCards.find(card => card.deckCardId === this.state.activeCardId);
}
get isProlificBaselineGame() {
return this.state.type === "prolificBaseline";
}
get lowResSystemHealth() {
if (this.state.systemHealth === 0) {
return 0;
}
return Math.floor((this.state.systemHealth - 1) / 5) + 1;
}
get lowResSystemHealthText() {
const map: Record<number, string> = {
0: "FATAL",
1: "CRITICAL",
2: "POOR!",
3: "OKAY!",
4: "GOOD!",
5: "GREAT",
};
return map[this.lowResSystemHealth];
}
handleInvest(investment: number) {
this.pointsGained = this.state.player.resources - investment;
this.systemHealthGained = investment;
Expand Down
29 changes: 17 additions & 12 deletions client/src/components/sologame/GameOver.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
<div v-if="isVictory" class="mb-5">
<h1 class="text-success">Success</h1>
<p>
You've successfully passed the rigors and challenges of this Port of Mars solo trial.
Congratulations!
{{ victoryText }}
</p>
</div>
<div v-else class="mb-5">
<h1 class="text-danger">Game Over</h1>
<p>Unfortunately you were unable to withstand the perils of Mars. Please try again!</p>
{{ defeatText }}
</div>
<div class="d-flex mb-5">
<div class="w-50 pr-3">
Expand All @@ -31,13 +30,13 @@
</div>
</div>
</div>
<b-button variant="primary" size="lg" @click="reload" class="mr-3">
<b-button variant="primary" size="lg" @click="$emit('continue')" class="mr-3">
<h4 class="mb-0">
<b-icon-arrow-clockwise />
Play again
<b-icon-arrow-clockwise v-if="!continueText" />
{{ continueText || "Play again" }}
</h4>
</b-button>
<b-button variant="secondary" size="lg" @click="gotoHighScorePage"
<b-button v-if="showHighScores" variant="secondary" size="lg" @click="gotoHighScorePage"
><h4 class="mb-0">
<b-icon-trophy scale="0.75" />
High Scores
Expand All @@ -61,16 +60,22 @@ export default class GameOver extends Vue {
@Prop() status!: string;
@Prop() points!: number;
@Prop() round!: number;
@Prop({ default: true }) showHighScores!: boolean;
@Prop() continueText?: string;
@Prop({
default:
"You've successfully passed the rigors and challenges of this Port of Mars solo trial. Congratulations!",
})
victoryText!: string;
@Prop({
default: "Unfortunately you were unable to withstand the perils of Mars. Please try again!",
})
defeatText!: string;
get isVictory() {
return this.status === "victory";
}
reload() {
// FIXME: is a full refresh required or would `this.$router.go(0)` or equivalent cache-busting router call work?
window.location.reload();
}
gotoHighScorePage() {
this.$router.push({ name: LEADERBOARD_PAGE, params: { showSolo: true } as any });
}
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/sologame/SegmentedBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<b-icon icon="plus-lg" scale="1.5" />
</button>
<VFDNumberDisplay
:value="value"
:value="customTextDisplay || value"
:digits="2"
class="ml-3"
:size="numberSize"
Expand Down Expand Up @@ -71,6 +71,7 @@ export default class SegmentedBar extends Vue {
@Prop({ default: "" }) label!: string;
@Prop({ default: "lg" }) size!: "sm" | "md" | "lg";
@Prop({ default: "green" }) variant!: "green" | "blue" | "red" | "yellow";
@Prop({ default: "" }) customTextDisplay!: string;
dragging = false;
Expand Down
Loading

0 comments on commit 7dae91a

Please sign in to comment.