Skip to content

Commit 7ae9bdf

Browse files
Merge pull request #57 from a2i2/nick/interruption-text-changes
Update the various text based off if its the 1st or 2nd attempt [CON-2665]
2 parents 9e54f3a + ff3f983 commit 7ae9bdf

File tree

10 files changed

+129
-31
lines changed

10 files changed

+129
-31
lines changed

EEFRT Demo Android/app/src/main/java/ai/a2i2/conductor/effrtdemoandroid/persistence/GameStorage.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ai.a2i2.conductor.effrtdemoandroid.persistence
22

3+
import ai.a2i2.conductor.effrtdemoandroid.util.GameConfigUtils
34
import android.content.Context
45
import hu.autsoft.krate.SimpleKrate
56
import hu.autsoft.krate.booleanPref
@@ -18,9 +19,16 @@ data class GameCache(
1819
val randTrialsIdx: List<Int>? = null,
1920
val trialSeqFilename: String? = null,
2021
var calibrationComplete: Boolean = false,
21-
var interruptionTimestamp: Long? = null
22+
var interruptionTimestamp: Long? = null,
23+
var attemptCount: Int = 1
2224
) {
2325
fun isResumeTrialAvailable(context: Context): Boolean {
26+
// Allow the user to resume from the beginning if triggering 2A interruption scenario
27+
val gameStorage = GameStorage(context)
28+
if (!practiceComplete && !gameStorage.gameMarkedAsComplete && gameStorage.eefrtAttemptCount == GameConfigUtils.MAX_EEFRT_ATTEMPTS) {
29+
return true
30+
}
31+
2432
return (practiceComplete || trialNumber > 0) && !GameStorage(context).gameMarkedAsComplete
2533
}
2634
}

EEFRT Demo Android/app/src/main/java/ai/a2i2/conductor/effrtdemoandroid/ui/EefrtScreen.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import ai.a2i2.conductor.effrtdemoandroid.persistence.PracticeTaskAttempt
66
import ai.a2i2.conductor.effrtdemoandroid.persistence.TaskAttempt
77
import ai.a2i2.conductor.effrtdemoandroid.ui.data.CloseMessage
88
import ai.a2i2.conductor.effrtdemoandroid.ui.data.EefrtScreenViewModel
9+
import ai.a2i2.conductor.effrtdemoandroid.util.GameConfigUtils
910
import android.annotation.SuppressLint
1011
import android.content.Context
1112
import android.os.Handler
@@ -120,6 +121,7 @@ fun EefrtScreen(
120121
cache.calibrationComplete = true
121122
cache.maxPressCount = gameStorage.calibratedMaxPressCount!!
122123
}
124+
cache.attemptCount = gameStorage.eefrtAttemptCount
123125

124126
val json = Json {
125127
encodeDefaults = true
@@ -261,6 +263,7 @@ private fun handleMessage(
261263
)
262264
val currentAttemptCount = GameStorage(context).eefrtAttemptCount
263265
viewModel.updateEEFRTAttemptCount(context, currentAttemptCount + 1)
266+
GameStorage(context).cachedGameState?.attemptCount++
264267
}
265268

266269
// ensure the game data is reset if we've actually closed the task, similar scenario to the shouldShowExitDialog
@@ -269,8 +272,22 @@ private fun handleMessage(
269272
TAG,
270273
"Task will be restarted on next load"
271274
)
272-
viewModel.setCurrentGameState(context, null)
273-
viewModel.resumeTrialAvailable.value = false
275+
276+
// Allow the resume button to appear if they trigger the 2A interruption
277+
// Need to check for the trialNumber >=2 since the trial number is already incremented before this check is done
278+
// The '2' value below being compared to the current trial number is generated due to the following:
279+
// 1 (index of trial 2) + 1 (above increment to trial number which occurs before the interruption dialog is shown)
280+
val gameStorage = GameStorage(context)
281+
val cache = gameStorage.cachedGameState
282+
if (cache != null && cache.practiceComplete && cache.trialNumber <= 2) {
283+
viewModel.setCurrentGameState(context, GameCache())
284+
285+
// only show the resume button if its within the 2 attempts
286+
viewModel.resumeTrialAvailable.value = gameStorage.eefrtAttemptCount <= GameConfigUtils.MAX_EEFRT_ATTEMPTS
287+
} else {
288+
viewModel.setCurrentGameState(context, null)
289+
viewModel.resumeTrialAvailable.value = false
290+
}
274291
}
275292

276293
if (closeMessage.shouldShowExitDialog) {

EEFRT Demo Android/app/src/main/java/ai/a2i2/conductor/effrtdemoandroid/ui/data/EefrtScreenViewModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ class EefrtScreenViewModel(
141141
)
142142
val currentValue = GameStorage(context).eefrtAttemptCount
143143
updateEEFRTAttemptCount(context, currentValue + 1)
144+
GameStorage(context).cachedGameState?.attemptCount++
144145
}
145146

146147
if (it.taskRequiresRestart) {

EEFRT Demo iOS/EEFRT Demo/EEFRTViewController.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ class EEFRTViewController: UIViewController {
145145
cache.calibrationComplete = calibrationComplete
146146
cache.maxPressCount = calibratedMaxPressCount
147147
}
148+
cache.attemptCount = Defaults.eefrtAttemptCount
148149

149150
if let stringifiedGameCache = try? cache.stringify() {
150151
let gameCacheJsString = "window.setupGameWithCache(\(stringifiedGameCache));"
@@ -204,6 +205,7 @@ class EEFRTViewController: UIViewController {
204205
// increment attempt count in the main app
205206
os_log(.debug, "Incremented attempt count")
206207
Defaults.eefrtAttemptCount += 1
208+
Defaults.gameCache?.attemptCount += 1
207209
}
208210

209211
if closeMessage.taskRequiresRestart {
@@ -250,13 +252,23 @@ extension EEFRTViewController: WKScriptMessageHandler {
250252
// increment attempt count - in main app
251253
os_log(.debug, "Incremented attempt count")
252254
Defaults.eefrtAttemptCount += 1
255+
Defaults.gameCache?.attemptCount += 1
253256
}
254257

255258
// ensure the game data is reset if we've actually closed the task, similar scenario to the shouldShowExitDialog
256259
if !closeMessage.shouldShowExitDialog, closeMessage.taskRequiresRestart {
257260
// the task needs to be restarted
258261
os_log(.debug, "Task will be restarted on next load")
259-
Defaults.gameCache = nil
262+
263+
// Allow the resume button to appear if they trigger the 2A interruption
264+
// Need to check for the trialNumber >=2 since the trial number is already incremented before this check is done
265+
// The '2' value below being compared to the current trial number is generated due to the following:
266+
// 1 (index of trial 2) + 1 (above increment to trial number which occurs before the interruption dialog is shown)
267+
if let cache = Defaults.gameCache, cache.practiceComplete, cache.trialNumber <= 2 {
268+
Defaults.gameCache = GameCache()
269+
} else {
270+
Defaults.gameCache = nil
271+
}
260272
}
261273

262274
if closeMessage.shouldShowExitDialog {

EEFRT Demo iOS/EEFRT Demo/Models/GameCache.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ struct GameCache: Codable, DefaultsSerializable {
1111
var trialSeqFilename: String?
1212
var calibrationComplete: Bool
1313
var interruptionTimestamp: Int64?
14+
var attemptCount: Int
1415

1516
// Default initializer with default values
1617
init(
@@ -22,7 +23,8 @@ struct GameCache: Codable, DefaultsSerializable {
2223
randTrialsIdx: [Int]? = nil,
2324
trialSeqFilename: String? = nil,
2425
calibrationComplete: Bool = false,
25-
interuptionTimestamp: Int64? = nil
26+
interuptionTimestamp: Int64? = nil,
27+
attemptCount: Int = 1
2628
) {
2729
self.practiceComplete = practiceComplete
2830
self.trialNumber = trialNumber
@@ -33,6 +35,7 @@ struct GameCache: Codable, DefaultsSerializable {
3335
self.trialSeqFilename = trialSeqFilename
3436
self.calibrationComplete = calibrationComplete
3537
self.interruptionTimestamp = interuptionTimestamp
38+
self.attemptCount = attemptCount
3639
}
3740

3841
// Decoder initializer
@@ -47,6 +50,7 @@ struct GameCache: Codable, DefaultsSerializable {
4750
self.trialSeqFilename = try container.decodeIfPresent(String.self, forKey: .trialSeqFilename)
4851
self.calibrationComplete = try container.decode(Bool.self, forKey: .calibrationComplete)
4952
self.interruptionTimestamp = try container.decodeIfPresent(Int64.self, forKey: .interruptionTimestamp)
53+
self.attemptCount = try container.decode(Int.self, forKey: .attemptCount)
5054
}
5155

5256
func encode(to encoder: any Encoder) throws {
@@ -60,6 +64,7 @@ struct GameCache: Codable, DefaultsSerializable {
6064
try container.encodeIfPresent(trialSeqFilename, forKey: .trialSeqFilename)
6165
try container.encodeIfPresent(calibrationComplete, forKey: .calibrationComplete)
6266
try container.encodeIfPresent(interruptionTimestamp, forKey: .interruptionTimestamp)
67+
try container.encode(attemptCount, forKey: .attemptCount)
6368
}
6469

6570
private enum CodingKeys: String, CodingKey {
@@ -72,15 +77,21 @@ struct GameCache: Codable, DefaultsSerializable {
7277
case trialSeqFilename
7378
case calibrationComplete
7479
case interruptionTimestamp
80+
case attemptCount
7581
}
7682

7783
func stringify() throws -> String {
7884
let encoder = JSONEncoder()
7985
let data = try encoder.encode(self)
8086
return String(decoding: data, as: UTF8.self)
8187
}
82-
88+
8389
func isResumeTrialAvailable() -> Bool {
84-
(practiceComplete || trialNumber > 0) && !Defaults.gameMarkedAsComplete
90+
// Allow the user to resume from the beginning if triggering 2A interruption scenario
91+
if practiceComplete == false, !Defaults.gameMarkedAsComplete, Defaults.eefrtAttemptCount == GameConfigUtils.maxEefrtAttempts {
92+
return true
93+
}
94+
95+
return (practiceComplete || trialNumber > 0) && !Defaults.gameMarkedAsComplete
8596
}
8697
}

public/src/elements/BottomScreenPanel.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const TIMEOUT_TAG = 'bottomScreenTimeout';
77
export const ARE_YOU_THERE_TAG = 'bottomScreenAreYouThere';
88
export const EXIT_TASK_TAG = 'bottomScreenExitTask';
99
export const GAME_COMPLETE_TAG = 'bottomScreenGameComplete';
10+
export const TASK_INTERRUPTED_TAG = 'bottomScreenTaskInterrupted';
1011

1112
export default class BottomScreenPanel {
1213
constructor(scene, x, titleString, subtitleString, bottomButtonString, breakTimeMS, tag, onContinuePressed = () => {}, onTimeout = () => {}) {

public/src/embedContext/GameCache.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export default class GameCache {
22
static cache = null;
33

4-
constructor(practiceComplete, trialNumber, maxPressCount, coinRunningTotal, trialResults, randTrialsIdx, trialSeqFilename, calibrationComplete, interruptionTimestamp) {
4+
constructor(practiceComplete, trialNumber, maxPressCount, coinRunningTotal, trialResults, randTrialsIdx, trialSeqFilename, calibrationComplete, interruptionTimestamp, attemptCount) {
55
this.practiceComplete = practiceComplete;
66
this.trialNumber = trialNumber;
77
this.maxPressCount = maxPressCount;
@@ -11,6 +11,7 @@ export default class GameCache {
1111
this.trialSeqFilename = trialSeqFilename;
1212
this.calibrationComplete = calibrationComplete;
1313
this.interruptionTimestamp = interruptionTimestamp;
14+
this.attemptCount = attemptCount;
1415
}
1516

1617
stringify() {

public/src/scenes/mainTask.js

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { POWER_UP_COMPLETE_KEY } from "../elements/PowerPanel.js";
2525
import GameCache from "../embedContext/GameCache.js";
2626
import TaskAttempt from "../embedContext/TaskAttempt.js";
2727
import { POWER_COUNTDOWN_KEY } from "../elements/CountdownPanel.js";
28-
import { BREAK_TAG, TIMEOUT_TAG, ARE_YOU_THERE_TAG, EXIT_TASK_TAG, GAME_COMPLETE_TAG } from "../elements/BottomScreenPanel.js";
28+
import { BREAK_TAG, TIMEOUT_TAG, ARE_YOU_THERE_TAG, EXIT_TASK_TAG, GAME_COMPLETE_TAG, TASK_INTERRUPTED_TAG } from "../elements/BottomScreenPanel.js";
2929
import InteruptionsHandler from "../embedContext/InteruptionsHandler.js";
3030
import CloseMessage from "../embedContext/CloseMessage.js";
3131

@@ -763,7 +763,7 @@ var trialEnd = function () {
763763
// if an interruption occured and we need to show the are you still there dialog, show it
764764
else if (this.interruptionShowAreYouThereDialog == true && !isLastTrial) {
765765
stopPlayer(this);
766-
showMissedTrialDialog(this);
766+
showTaskInterruptedDialog(this);
767767
this.interruptionShowAreYouThereDialog = false;
768768
}
769769
// if an interruption occured and we need to show the times up dialog, show it
@@ -822,7 +822,7 @@ var loadGameFromCache = function(context) {
822822
const cache = GameCache.cache;
823823
if (cache == null) {
824824
// if no cache to load from, create a new one with the default values
825-
GameCache.cache = new GameCache(true, 0, undefined, 0, {}, [], context.trialSequenceFile, null);
825+
GameCache.cache = new GameCache(true, 0, undefined, 0, {}, [], context.trialSequenceFile, false, null, 1);
826826
return;
827827
}
828828

@@ -948,7 +948,7 @@ var showTimeUpDialog = function(context) {
948948
return;
949949
}
950950

951-
let timeoutMessage = "Unfortunately you've run out of time to continue the this task. Try again to recieve a bonus payment.";
951+
let timeoutMessage = "Unfortunately you\'ve run out of time to continue the this task. Try again to recieve a bonus payment.";
952952
let showExitDialog = false;
953953
let incrementAttemptCount = true;
954954

@@ -988,13 +988,16 @@ var showBreakDialog = function(context) {
988988
}
989989

990990
var showExitTaskDialog = function(context) {
991-
let retryTaskText = "You've been away too long, and may need to try again.";
991+
let isSecondAttempt = GameCache.cache?.attemptCount == 2;
992+
let retryTaskText = isSecondAttempt ? "You\'ve been away too long, and it was your second attempt. Unfortunately you wont be able to earn any more coins." : "You\'ve been away too long, try again for a chance to earn more coins."
993+
let titleText = isSecondAttempt ? "Exit task" : "Try again"
994+
992995
let showExitDialog = false;
993996
let incrementAttemptCount = true;
994997

995998
showBottomScreenPanel(
996999
context,
997-
"Retry task",
1000+
titleText,
9981001
retryTaskText,
9991002
"EXIT",
10001003
null,
@@ -1005,7 +1008,7 @@ var showExitTaskDialog = function(context) {
10051008
}
10061009

10071010
var showTaskCompleteDialog = function(context) {
1008-
let taskCompleteText = "You've reached the minimum required number of completed trials but due to an interruption you cannot progress through the rest of the trials. You will still recieve your bonus payment.";
1011+
let taskCompleteText = "You\'ve reached the minimum required number of completed rounds but due to an interruption you cannot progress through the rest of the rounds. You will still recieve your bonus payment.";
10091012

10101013
showBottomScreenPanel(
10111014
context,
@@ -1019,6 +1022,26 @@ var showTaskCompleteDialog = function(context) {
10191022
);
10201023
}
10211024

1025+
var showTaskInterruptedDialog = function(context) {
1026+
// check to see if this is the first attempt and show a different message explaining that they cannot return if its the 2nd attempt
1027+
let cache = GameCache.cache;
1028+
var taskInterruptedText = "Ready to continue? Keep going in the next 2 mins to keep collecting coins.";
1029+
if (cache != null && cache.attemptCount > 1) {
1030+
taskInterruptedText = "Ready to continue? This is your last attempt to collect more coins.";
1031+
}
1032+
1033+
showBottomScreenPanel(
1034+
context,
1035+
"Task interrupted",
1036+
taskInterruptedText,
1037+
"CONTINUE",
1038+
breakTime,
1039+
TASK_INTERRUPTED_TAG,
1040+
() => { continueGameAfterBreak(context); },
1041+
() => { showTimeUpDialog(context); }
1042+
);
1043+
}
1044+
10221045
var showBottomScreenPanel = function(context, titleText, subtitleText, bottomButtonText, countdownTimerMS, tag, onContinuePressed, onTimeout) {
10231046
let camera = context.cameras.main;
10241047

@@ -1157,9 +1180,10 @@ var saveData = function(context) {
11571180

11581181
// if the user has reached at least 80% then we can mark the calibration as completed
11591182
var calibrationComplete = GameCache.cache?.calibrationComplete == true || trialNo + 1 >= nTrials * taskRewardsPayoutThreshold;
1183+
let eefrtAttemptCount = GameCache.cache?.attemptCount ?? 1;
11601184

11611185
// notify the native apps of what the current game state is so they can cache it
1162-
let currentGameState = new GameCache(true, trialNo + 1, thresholdMax, nCoins, coinChoices, randTrialsIdx, context.trialSequenceFile, calibrationComplete);
1186+
let currentGameState = new GameCache(true, trialNo + 1, thresholdMax, nCoins, coinChoices, randTrialsIdx, context.trialSequenceFile, calibrationComplete, null, eefrtAttemptCount);
11631187
GameCache.cache = currentGameState;
11641188
EmbedContext.sendMessage('currentGameCache', currentGameState.stringify());
11651189
}

public/src/scenes/practiceTask.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Coins from "../elements/coins.js";
77
import RouteSelectorPanel from "../elements/RouteSelectorPanel.js";
88
import ProgressBar from "../elements/progressBar.js";
99
import PowerPanel, { PRACTICE_POWER_UP_COMPLETE_KEY } from "../elements/PowerPanel.js";
10+
import { ENTRY_POINT_PRACTICE } from "../scenes/startTaskScene.js";
1011

1112
// import our custom events center for passsing info between scenes annd relevant data saving function
1213
import eventsCenter from '../eventsCenter.js'
@@ -22,6 +23,7 @@ import InteruptionsHandler from "../embedContext/InteruptionsHandler.js";
2223
import CloseMessage from "../embedContext/CloseMessage.js";
2324
import BottomScreenPanel from "../elements/BottomScreenPanel.js";
2425
import { EXIT_TASK_TAG } from "../elements/BottomScreenPanel.js"
26+
import StartTaskScene from "./startTaskScene.js";
2527

2628
// initialize some global vars
2729
var gameHeight;
@@ -302,6 +304,9 @@ export default class PracticeTask extends BaseScene {
302304
Once we complete all 4 practice trials we we want to save the maxPressCount to the cache and the registry so it can be used in the main trials.
303305
*/
304306
if (pracTrial == nPracTrials) {
307+
// let the start task scene know we arrived from the practice screens
308+
StartTaskScene.entryPoint = ENTRY_POINT_PRACTICE;
309+
305310
let calibrationComplete = false;
306311
if (GameCache.cache?.calibrationComplete == true) {
307312
calibrationComplete = true;
@@ -310,7 +315,8 @@ export default class PracticeTask extends BaseScene {
310315

311316
// signal to the cache that the practice is complete
312317
let trialSeqFilename = GameCache.cache?.trialSeqFilename || null;
313-
let cache = new GameCache(true, 0, maxPressCount, 0, {}, null, trialSeqFilename, calibrationComplete, null)
318+
let eefrtAttemptCount = GameCache.cache?.attemptCount ?? 1;
319+
let cache = new GameCache(true, 0, maxPressCount, 0, {}, null, trialSeqFilename, calibrationComplete, null, eefrtAttemptCount)
314320
GameCache.cache = cache;
315321
EmbedContext.sendMessage('currentGameCache', cache.stringify());
316322

@@ -331,9 +337,10 @@ export default class PracticeTask extends BaseScene {
331337
3. If the power up scene is active
332338
4. If the user has already completed the trial and is crossing the bridge, show the dialog once pickle reaches the end of the bridge
333339
*/
334-
let isLastPracticeTrial = pracTrial == nPracTrials - 1;
340+
let isLastPracticeTrial = pracTrial == nPracTrials - 1;
341+
let routeOrPowerAnimationKeys = ['powerup', 'wait'];
335342

336-
if (this.player.sprite.x <= decisionPointX && this.player.sprite.anims.currentAnim.key == 'run' && !isLastPracticeTrial) {
343+
if (this.player.sprite.x <= decisionPointX && this.player.sprite.anims.currentAnim.key == 'run' && !isLastPracticeTrial) {
337344
console.log('1A-A');
338345

339346
// no active panel, stop the player and show the dialog
@@ -356,7 +363,7 @@ export default class PracticeTask extends BaseScene {
356363
// show the exit task dialog in its place
357364
stopPlayer(this);
358365
showExitTaskDialog(this);
359-
} else if (this.powerPanel != null && ['powerup', 'wait'].includes(this.player.sprite.anims.currentAnim.key) && !isLastPracticeTrial) {
366+
} else if (this.powerPanel != null && routeOrPowerAnimationKeys.includes(this.player.sprite.anims.currentAnim.key) && !isLastPracticeTrial) {
360367
console.log('1A-C');
361368

362369
// mark that an interruption occured so the feedback message dismiss handler doesn't run

0 commit comments

Comments
 (0)