Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

additional solo game configuration and integration with prolific #951

Merged
merged 19 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading