1
0
mirror of synced 2025-02-07 23:11:19 +01:00

Cleaned up scratch sheet code, should be ready for live now. Have tested in-game, story mode, ghost mode, time attack with no issues.

This commit is contained in:
Damon Murdoch 2022-07-23 02:10:56 +10:00
parent da1bf27c9d
commit 0a46d28d8c
3 changed files with 247 additions and 274 deletions

View File

@ -2,186 +2,186 @@
// learn more about it in the docs: https://pris.ly/d/prisma-schema // learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
} }
datasource db { datasource db {
provider = "postgresql" provider = "postgresql"
url = env("POSTGRES_URL") url = env("POSTGRES_URL")
} }
model User { model User {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
chipId String @unique chipId String @unique
accessCode String accessCode String
cars Car[] cars Car[]
carOrder Int[] carOrder Int[]
items UserItem[] items UserItem[]
tutorials Boolean[] tutorials Boolean[]
userBanned Boolean @default(false) userBanned Boolean @default(false)
bookmarks Int[] bookmarks Int[]
ScratchSheet ScratchSheet[] ScratchSheet ScratchSheet[]
currentSheet Int @default(1) currentSheet Int @default(1)
lastSheet Int @default(1) lastScratched Int @default(0) // Timestamp
lastScratched Int @default(0) // Timestamp
} }
model ScratchSheet { model ScratchSheet {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
User User @relation(fields: [userId], references: [id]) User User @relation(fields: [userId], references: [id])
userId Int userId Int
sheetNo Int // Player's sheet number (i.e. first sheet) sheetNo Int // Player's sheet number (i.e. first sheet)
squares ScratchSquare[] squares ScratchSquare[]
} }
model ScratchSquare { model ScratchSquare {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
Sheet ScratchSheet @relation(fields: [sheetId], references: [id]) Sheet ScratchSheet @relation(fields: [sheetId], references: [id])
sheetId Int sheetId Int
category Int category Int
itemId Int itemId Int
earned Boolean earned Boolean
} }
model UserItem { model UserItem {
userItemId Int @id @default(autoincrement()) userItemId Int @id @default(autoincrement())
category Int category Int
itemId Int itemId Int
User User @relation(fields: [userId], references: [id]) User User @relation(fields: [userId], references: [id])
userId Int userId Int
type Int @default(0) type Int @default(0)
earnedAt Int @default(0) earnedAt Int @default(0)
} }
model Car { model Car {
user User @relation(fields: [userId], references: [id]) user User @relation(fields: [userId], references: [id])
userId Int userId Int
// This is the Car object itself // This is the Car object itself
carId Int @id @default(autoincrement()) carId Int @id @default(autoincrement())
name String name String
manufacturer Int manufacturer Int
regionId Int @default(0) regionId Int @default(0)
model Int model Int
visualModel Int visualModel Int
customColor Int @default(0) customColor Int @default(0)
defaultColor Int defaultColor Int
wheel Int @default(0) wheel Int @default(0)
wheelColor Int @default(0) wheelColor Int @default(0)
aero Int @default(0) aero Int @default(0)
bonnet Int @default(0) bonnet Int @default(0)
wing Int @default(0) wing Int @default(0)
mirror Int @default(0) mirror Int @default(0)
neon Int @default(0) neon Int @default(0)
trunk Int @default(0) trunk Int @default(0)
plate Int @default(0) plate Int @default(0)
plateColor Int @default(0) plateColor Int @default(0)
plateNumber Int @default(0) plateNumber Int @default(0)
tunePower Int @default(0) tunePower Int @default(0)
tuneHandling Int @default(0) tuneHandling Int @default(0)
title String @default("New Car") title String @default("New Car")
level Int @default(0) level Int @default(0)
windowSticker Boolean @default(false) windowSticker Boolean @default(false)
windowStickerString String @default("") windowStickerString String @default("")
windowStickerFont Int @default(0) windowStickerFont Int @default(0)
rivalMarker Int @default(0) rivalMarker Int @default(0)
lastPlayedAt Int @default(0) lastPlayedAt Int @default(0)
aura Int @default(0) aura Int @default(0)
auraMotif Int @default(0) auraMotif Int @default(0)
ghostLevel Int @default(1) ghostLevel Int @default(1)
// This is more data about the car // This is more data about the car
tuningPoints Int @default(0) tuningPoints Int @default(0)
odometer Int @default(0) odometer Int @default(0)
playCount Int @default(0) playCount Int @default(0)
earnedCustomColor Boolean @default(false) earnedCustomColor Boolean @default(false)
carSettingsDbId Int @unique carSettingsDbId Int @unique
settings CarSettings @relation(fields: [carSettingsDbId], references: [dbId]) settings CarSettings @relation(fields: [carSettingsDbId], references: [dbId])
vsPlayCount Int @default(0) vsPlayCount Int @default(0)
vsBurstCount Int @default(0) vsBurstCount Int @default(0)
vsStarCount Int @default(0) vsStarCount Int @default(0)
vsCoolOrWild Int @default(0) vsCoolOrWild Int @default(0)
vsSmoothOrRough Int @default(0) vsSmoothOrRough Int @default(0)
vsTripleStarMedals Int @default(0) vsTripleStarMedals Int @default(0)
vsDoubleStarMedals Int @default(0) vsDoubleStarMedals Int @default(0)
vsSingleStarMedals Int @default(0) vsSingleStarMedals Int @default(0)
vsPlainMedals Int @default(0) vsPlainMedals Int @default(0)
rgPlayCount Int @default(0) rgPlayCount Int @default(0)
rgWinCount Int @default(0) rgWinCount Int @default(0)
rgTrophy Int @default(0) rgTrophy Int @default(0)
rgScore Int @default(0) rgScore Int @default(0)
rgStamp Int @default(0) rgStamp Int @default(0)
rgAcquireAllCrowns Boolean @default(false) rgAcquireAllCrowns Boolean @default(false)
dressupLevel Int @default(0) rgRegionMapScore Int[]
dressupPoint Int @default(0) dressupLevel Int @default(0)
stPlayCount Int @default(0) dressupPoint Int @default(0)
stClearBits Int @default(0) stPlayCount Int @default(0)
stClearDivCount Int @default(0) stClearBits Int @default(0)
stClearCount Int @default(0) stClearDivCount Int @default(0)
stLoseBits BigInt @default(0) stClearCount Int @default(0)
stConsecutiveWins Int @default(0) stLoseBits BigInt @default(0)
stConsecutiveWinsMax Int @default(0) stConsecutiveWins Int @default(0)
stCompleted100Episodes Boolean @default(false) stConsecutiveWinsMax Int @default(0)
stCompleted100Episodes Boolean @default(false)
items CarItem[] items CarItem[]
carStateDbId Int @unique carStateDbId Int @unique
state CarState @relation(fields: [carStateDbId], references: [dbId]) state CarState @relation(fields: [carStateDbId], references: [dbId])
TimeAttackRecord TimeAttackRecord[] TimeAttackRecord TimeAttackRecord[]
} }
model CarItem { model CarItem {
dbId Int @id @default(autoincrement()) dbId Int @id @default(autoincrement())
Car Car @relation(fields: [carId], references: [carId]) Car Car @relation(fields: [carId], references: [carId])
carId Int carId Int
category Int category Int
itemId Int itemId Int
amount Int amount Int
} }
model CarSettings { model CarSettings {
dbId Int @id @default(autoincrement()) dbId Int @id @default(autoincrement())
car Car? car Car?
view Boolean @default(true) view Boolean @default(true)
transmission Boolean @default(false) transmission Boolean @default(false)
retire Boolean @default(false) retire Boolean @default(false)
meter Int @default(0) meter Int @default(0)
navigationMap Boolean @default(true) navigationMap Boolean @default(true)
volume Int @default(1) volume Int @default(1)
bgm Int @default(0) bgm Int @default(0)
nameplate Int @default(0) nameplate Int @default(0)
nameplateColor Int @default(0) nameplateColor Int @default(0)
terminalBackground Int @default(0) terminalBackground Int @default(0)
} }
model CarState { model CarState {
dbId Int @id @default(autoincrement()) dbId Int @id @default(autoincrement())
car Car? car Car?
hasOpponentGhost Boolean @default(false) hasOpponentGhost Boolean @default(false)
eventJoined Boolean @default(false) eventJoined Boolean @default(false)
transferred Boolean @default(false) transferred Boolean @default(false)
toBeDeleted Boolean @default(false) toBeDeleted Boolean @default(false)
} }
model TimeAttackRecord { model TimeAttackRecord {
dbId Int @id @default(autoincrement()) dbId Int @id @default(autoincrement())
car Car @relation(fields: [carId], references: [carId]) car Car @relation(fields: [carId], references: [carId])
carId Int carId Int
model Int // Car model, literally just the `model` field from Car model Int // Car model, literally just the `model` field from Car
time Int time Int
course Int course Int
isMorning Boolean isMorning Boolean
section1Time Int @map("section1Time") section1Time Int @map("section1Time")
section2Time Int @map("section2Time") section2Time Int @map("section2Time")
section3Time Int @map("section3Time") section3Time Int @map("section3Time")
section4Time Int @map("section4Time") section4Time Int @map("section4Time")
section5Time Int? @map("section5Time") section5Time Int? @map("section5Time")
section6Time Int? @map("section6Time") section6Time Int? @map("section6Time")
section7Time Int? @map("section7Time") section7Time Int? @map("section7Time")
tunePower Int @default(0) // Car Power tunePower Int @default(0) // Car Power
tuneHandling Int @default(0) // Car Handling tuneHandling Int @default(0) // Car Handling
} }

View File

@ -13,6 +13,7 @@ import { envelopeItemTypeToDataCategory } from "@sentry/utils";
export default class GameModule extends Module { export default class GameModule extends Module {
register(app: Application): void { register(app: Application): void {
app.post('/method/save_game_result', async (req, res) => { app.post('/method/save_game_result', async (req, res) => {
let body = wm.wm.protobuf.SaveGameResultRequest.decode(req.body); let body = wm.wm.protobuf.SaveGameResultRequest.decode(req.body);
let car = await prisma.car.findFirst({ let car = await prisma.car.findFirst({
@ -513,12 +514,25 @@ export default class GameModule extends Module {
} }
}) })
// While the user's scratch sheet count is greater console.log("Current sheet count:", scratchSheetCount);
// than the number of scratch sheets generated
while (scratchSheetCount < user!.lastSheet) // If the user has no scratch sheets
if (scratchSheetCount == 0)
{ {
console.log("Generating first sheet ...");
// Generate a new scratch sheet for the user // Generate a new scratch sheet for the user
await scratch.generateScratchSheet(user!.id, ++scratchSheetCount); await scratch.generateScratchSheet(user!.id, 1);
// Set the current scratch sheet to 1
await prisma.user.update({
where: {
id: user!.id
},
data: {
currentSheet: 1
}
});
} }
// Get the user's cars // Get the user's cars
@ -1013,7 +1027,7 @@ export default class GameModule extends Module {
} }
}); });
// Get the protobuf data for the scratch sheet // Get the updated scratch sheet proto
scratchSheetProto = await scratch.getScratchSheetProto(body.userId); scratchSheetProto = await scratch.getScratchSheetProto(body.userId);
// User is defined // User is defined
@ -1030,27 +1044,11 @@ export default class GameModule extends Module {
} }
else // Otherwise, daily scratches else // Otherwise, daily scratches
{ {
// Get the days since epoch // If a day has passed, allow the user to scratch again
let daysSinceEpoch = (date: Date) => { scratched = scratch.dayPassed(
return Math.floor(Number(date)/8.64e7); new Date(date*1000), // Todays date
}; new Date(user.lastScratched*1000) // Last Scratched date
);
// Get the days since epoch for current date, last scratched date
let currentDate = daysSinceEpoch(new Date(date*1000));
let lastScratched = daysSinceEpoch(new Date(user.lastScratched*1000));
// If unlimited scratches are enabled, or it is at least
// the next day since the user last scratched off
if (currentDate > lastScratched)
{
// User can scratch again
scratched = 0;
}
else // It is not the next day / other error
{
// User cannot scratch
scratched = 1;
}
} }
} }
} }
@ -1197,29 +1195,8 @@ export default class GameModule extends Module {
// If the box we uncovered is the car // If the box we uncovered is the car
if (scratchSquare.category == 201) if (scratchSquare.category == 201)
{ {
// If the current sheet is the last sheet // Generate a new scratch sheet for the user
if (body.targetSheet == user?.lastSheet) await scratch.generateScratchSheet(body.userId, body.targetSheet + 1)
{
// Update the last sheet value
let lastSheet = body.targetSheet + 1;
// Increment the player's maximum scratch sheets
await prisma.user.update({
where: {
id: body.userId
},
data: {
lastSheet: lastSheet
}
});
// Generate a new scratch sheet for the user
await scratch.generateScratchSheet(body.userId, lastSheet)
}
else // We are not on the last sheet
{
// Should never happen, do nothing
}
} }
} }
catch (error) // Failed to update scratch sheet catch (error) // Failed to update scratch sheet
@ -1303,13 +1280,6 @@ export default class GameModule extends Module {
} }
}); });
let c = await prisma.car.update({
where: {
carId: body.carId
},
data: saveEx
});
let msg = { let msg = {
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS, error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS,
} }
@ -1769,6 +1739,10 @@ export default class GameModule extends Module {
}) })
app.post('/method/update_user_session', (req, res) => { app.post('/method/update_user_session', (req, res) => {
// Get the request body
// let body = wm.wm.protobuf.UpdateUserSessionRequest.decode(req.body);
let msg = { let msg = {
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS, error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS,
} }
@ -1799,6 +1773,7 @@ export default class GameModule extends Module {
.status(200); .status(200);
r.send(Buffer.from(end)); r.send(Buffer.from(end));
}) })
app.post('/method/search_cars_by_level', async (req, res) => { app.post('/method/search_cars_by_level', async (req, res) => {
let body = wm.wm.protobuf.SearchCarsByLevelRequest.decode(req.body); let body = wm.wm.protobuf.SearchCarsByLevelRequest.decode(req.body);
console.log(body); console.log(body);
@ -1886,6 +1861,7 @@ export default class GameModule extends Module {
.status(200); .status(200);
r.send(Buffer.from(end)); r.send(Buffer.from(end));
}) })
app.post('/method/load_ghost_drive_data', async (req, res) => { app.post('/method/load_ghost_drive_data', async (req, res) => {
let body = wm.wm.protobuf.LoadGhostDriveDataRequest.decode(req.body); let body = wm.wm.protobuf.LoadGhostDriveDataRequest.decode(req.body);
console.log(body); console.log(body);

View File

@ -42,7 +42,8 @@ let scratchSheets = [
[26, 29], [26, 29], [26, 29], [26, 29], // BAT [26, 29], [26, 29], [26, 29], [26, 29], // BAT
[26, 2], [26, 2], [26, 2], [26, 2], // ANIMAL [26, 2], [26, 2], [26, 2], [26, 2], // ANIMAL
[26, 8], [26, 8], [26, 8], [26, 8], // PAINT SPLASH [26, 8], [26, 8], [26, 8], [26, 8], // PAINT SPLASH
], ]
/*
[ // Sheet 3 (Incomplete) [ // Sheet 3 (Incomplete)
[201, 1], // Hiace Van [201, 1], // Hiace Van
@ -137,6 +138,7 @@ let scratchSheets = [
[26, 11], [26, 11], [26, 11], [26, 11], // Steel [26, 11], [26, 11], [26, 11], [26, 11], // Steel
[26, 26], [26, 26], [26, 26], [26, 26], // Wall 2 [26, 26], [26, 26], [26, 26], [26, 26], // Wall 2
], ],
*/
] ]
// Terminal scratch cars only // Terminal scratch cars only
@ -161,28 +163,6 @@ export const stockTunedCars = [
29, 30, 31, 32, 33, 34, 35, 36 29, 30, 31, 32, 33, 34, 35, 36
]; ];
// Non-story racing meters
const nonStoryMeters = [
// Meters are added
// to scratch as a set
// Namco / Special Meters
[1,4],
// VSORG / Other Meters
[8, 9],
[10, 11],
[12, 13],
[14, 15],
[16, 17],
[18, 19],
[20, 21],
[22, 23],
[24, 25],
[26, 27]
];
// *** FUNCTIONS *** // *** FUNCTIONS ***
// Get a random integer within a range // Get a random integer within a range
@ -192,54 +172,12 @@ function getRandom(a: number, b: number)
return Math.floor(Math.random() * (b - a)) + a; return Math.floor(Math.random() * (b - a)) + a;
} }
// Generates a random scratch sheet // Get the days since epoch
// Contents: function daysSinceEpoch(date: Date)
// 1 Random Scratch Car
// 25 Random Scratch Stickers
// 24 Random Scratch Versus Markers
function getRandomScratchSheet ()
{ {
// Scratch items list // Return the days since the epoch
let items : number[][] = []; return Math.floor(Number(date)/8.64e7);
};
// Add the random scratch car
items.push([201, scratchCars[getRandom(0, scratchCars.length)]])
// Add the random scratch stickers
// Five different sticker styles
for(let i=0; i<5; i++)
{
// Get a random versus marker
let marker = getRandom(0, 61);
// Five different instances
for(let j=0; j<5; j++)
{
// Add marker to the list
items.push([25, marker])
}
}
// Add the random versus markers
// Six different marker styles
for(let i=0; i<6; i++)
{
// Get a random versus marker
let marker = getRandom(0, 81);
// Four different instances
for(let j=0; j<4; j++)
{
// Add marker to the list
items.push([26, marker])
}
}
// Return the items list
return items;
}
// Fisher yates shuffle for the scratch card elements // Fisher yates shuffle for the scratch card elements
function shuffleScratchSheet (array: number[][]) function shuffleScratchSheet (array: number[][])
@ -258,6 +196,64 @@ function shuffleScratchSheet (array: number[][])
return array; return array;
} }
// Generates a random scratch sheet
// Contents:
// 1 Random Scratch Car
// 25 Random Scratch Stickers
// 24 Random Scratch Versus Markers
function getRandomScratchSheet (carId: number)
{
// Scratch items list
let items : number[][] = [];
// Add the scratch car for the given index
items.push([201, scratchCars[carId % scratchCars.length]]);
// Add the random scratch stickers
// Five different sticker styles
for(let i=0; i<5; i++)
{
// Get a random versus marker
let sticker = getRandom(0, 60);
// Five different instances
for(let j=0; j<5; j++)
{
// Add marker to the list
items.push([25, sticker + 1])
}
}
// Add the random versus markers
// Six different marker styles
for(let i=0; i<6; i++)
{
// Get a random versus marker
let marker = getRandom(0, 80);
// Four different instances
for(let j=0; j<4; j++)
{
// Add marker to the list
items.push([26, marker + 1])
}
}
// Return the items list
return items;
}
// Get the days passed between dates 'a' and 'b'
export function dayPassed(a: Date, b: Date)
{
// Return 1 if a day has passed since the last scratch, 0 otherwise
return daysSinceEpoch(a) > daysSinceEpoch(b) ? 0 : 1;
}
// Async function for generating a new scratch sheet // Async function for generating a new scratch sheet
export async function generateScratchSheet (userId: number, sheetNo: number) export async function generateScratchSheet (userId: number, sheetNo: number)
{ {
@ -272,7 +268,7 @@ export async function generateScratchSheet (userId: number, sheetNo: number)
{ {
// More options maybe added in the future // More options maybe added in the future
case 0: // Same as actual game case 0: // Same as actual game (randomised after last set)
// If the sheet number has associated data // If the sheet number has associated data
if (scratchSheets.length >= sheetNo) if (scratchSheets.length >= sheetNo)
@ -282,15 +278,16 @@ export async function generateScratchSheet (userId: number, sheetNo: number)
} }
else // Sheet is out of range else // Sheet is out of range
{ {
console.log("No data for sheet:", sheetNo); // Generate a random (standard) scratch sheet
scratchItems = getRandomScratchSheet(sheetNo-1);
} }
break; break;
case 1: // Infinite random scratch sheets (Standard Items) case 1: // Infinite random scratch sheets (Standard Items)
// Generate a random (standard) scratch sheet // Generate a random (standard) scratch sheet
scratchItems = getRandomScratchSheet(); scratchItems = getRandomScratchSheet(sheetNo-1);
break;
default: // Not implemented default: // Not implemented
console.log("Method not implemented: " + scratchType); console.log("Method not implemented: " + scratchType);