1
0
mirror of https://github.com/shiroikitsu8/Bayshore_6r_legacy.git synced 2025-01-19 17:08:44 +01:00

Merge pull request #6 from damon-murdoch/master

Terminal Mode Features
This commit is contained in:
Luna 2022-07-20 16:44:34 +01:00 committed by GitHub
commit a2460fcb5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 837 additions and 143 deletions

View File

@ -4,6 +4,8 @@
"regionName": "Bayshore",
"serverIp": "127.0.0.1",
"gameOptions": {
"grantFullTuneTicketToNewUsers": 0
"grantFullTuneTicketToNewUsers": 0,
"grantAllScratchRewards": 0,
"grantBonusScratchCars": 0
}
}

View File

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

View File

@ -17,6 +17,17 @@ export interface UnixOptions {
}
export interface GameOptions {
// If set to 1, all scratch rewards will be granted to players (including cars)
grantAllScratchRewards: number;
// If set to 1, gives the player a random colour for each of the special cars.
// If set to 2, allows the player to pick any of the colours (more cluttered)
grantBonusScratchCars: number;
// If set to 1, all gift cars (i.e. S2000, S660, etc. will be fully tuned.)
// If set to 0, they will be left at their default tune (i.e. stock, basic tune, etc.)
giftCarsFullyTuned: number,
// Amount of full-tunes to grant to newly registered cards
grantFullTuneTicketToNewUsers: number;
}

View File

@ -3,9 +3,11 @@ import { Module } from "../module";
import * as wm from "../wmmt/wm.proto";
import * as svc from "../wmmt/service.proto";
import { prisma } from "..";
import { User } from "@prisma/client";
import { Car, User } from "@prisma/client";
import { Config } from "../config";
import Long from "long";
import { userInfo } from "os";
import { config } from "dotenv";
export default class GameModule extends Module {
register(app: Application): void {
@ -102,8 +104,6 @@ export default class GameModule extends Module {
}
case wm.wm.protobuf.GameMode.MODE_TIME_ATTACK:
{
console.log(body);
// If the game was not timed out / retired
if (!(body.retired || body.timeup)) {
@ -139,13 +139,15 @@ export default class GameModule extends Module {
section5Time: body!.taResult!.section_5Time,
section6Time: body!.taResult!.section_6Time,
section7Time: body!.taResult!.section_7Time,
tunePower: body!.car!.tunePower,
tuneHandling: body!.car!.tuneHandling
}
});
}
else // Creating a new record
{
console.log('Creating new time attack record');
await prisma.timeAttackRecord.create({
data: {
carId: body.carId,
@ -160,6 +162,8 @@ export default class GameModule extends Module {
section5Time: body!.taResult!.section_5Time,
section6Time: body!.taResult!.section_6Time,
section7Time: body!.taResult!.section_7Time,
tunePower: body!.car!.tunePower,
tuneHandling: body!.car!.tuneHandling
}
});
break;
@ -221,7 +225,9 @@ export default class GameModule extends Module {
})
app.post('/method/load_user', async (req, res) => {
let body = wm.wm.protobuf.LoadUserRequest.decode(req.body);
let user = await prisma.user.findFirst({
where: {
chipId: body.cardChipId,
@ -236,7 +242,10 @@ export default class GameModule extends Module {
unusedTickets: true
}
});
// No user returned
if (!user) {
console.log('no such user');
let msg = {
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS,
@ -334,7 +343,11 @@ export default class GameModule extends Module {
r.send(Buffer.from(end));
return;
}
// console.log(user);
let carStates = user.cars.map(e => e.state);
let tickets = (user.unusedTickets || []).map(x => {
return {
itemId: x.itemId,
@ -342,6 +355,7 @@ export default class GameModule extends Module {
category: x.category
}
});
let msg = {
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS,
numOfOwnedCars: user.cars.length,
@ -482,7 +496,7 @@ export default class GameModule extends Module {
r.send(Buffer.from(end));
})
//terminal specific
// Load upon enter terminal
app.post('/method/load_terminal_information', (req, res) => {
let msg = {
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS,
@ -503,13 +517,387 @@ export default class GameModule extends Module {
.status(200);
r.send(Buffer.from(end));
})
app.post('/method/load_scratch_information', (req, res) => {
// Load unrecieved user items
app.post('/method/load_unrecieved_user_items', (req, res) => {
// In future, might want to check db for player items
let msg = {
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS,
currentSheet: 21,
numOfScratched: 0,
owned_user_items: [
wm.wm.protobuf.UserItem.create({
category: 17,
itemId: 1
})
]
}
let resp = wm.wm.protobuf.LoadUnreceivedUserItemsResponse.encode(msg);
let end = resp.finish();
let r = res
.header('Server', 'v388 wangan')
.header('Content-Type', 'application/x-protobuf; revision=8053')
.header('Content-Length', end.length.toString())
.status(200);
r.send(Buffer.from(end));
})
// Load user bookmarks
app.post('/method/load_bookmarks', async (req, res) => {
// Get the save bookmark request
let body = wm.wm.protobuf.LoadBookmarksRequest.decode(req.body);
// Check if the user has any existing bookmarks
let user = await prisma.user.findFirst({
where: {
id: Number(body.userId)
}
});
// Car bookmarks placeholder
let cars : Car[] = [];
// User is not null
if (user)
{
// Loop over the bookmarked cars
for (let carId of user.bookmarks)
{
// Get the car with the bookmarked car id
let car = await prisma.car.findFirst({
where: {
carId: carId
}
});
// If the car is not null
if (car)
{
// Add the car to the cars list
cars.push(car);
}
}
}
let msg = {
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS,
cars: cars
}
let resp = wm.wm.protobuf.LoadBookmarksResponse.encode(msg);
let end = resp.finish();
let r = res
.header('Server', 'v388 wangan')
.header('Content-Type', 'application/x-protobuf; revision=8053')
.header('Content-Length', end.length.toString())
.status(200);
r.send(Buffer.from(end));
})
// Save user bookmarks
app.post('/method/save_bookmarks', async (req, res) => {
// Get the save bookmark request
let body = wm.wm.protobuf.SaveBookmarksRequest.decode(req.body);
// Update existing bookmarks
await prisma.user.update({
where: {
id: body.userId
},
data: {
bookmarks: body.cars
}
})
// Generate the response to the terminal (success messsage)
let resp = wm.wm.protobuf.LoadBookmarksResponse.encode({
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS
});
let end = resp.finish();
let r = res
.header('Server', 'v388 wangan')
.header('Content-Type', 'application/x-protobuf; revision=8053')
.header('Content-Length', end.length.toString())
.status(200);
r.send(Buffer.from(end));
})
// Car Summary Request (for bookmarks)
app.get('/resource/car_summary', async (req, res) => {
// Get the query from the request
let query = req.query;
// Get all of the cars matching the query
let cars = await prisma.car.findMany({
take: Number(query.limit),
where: {
name: {
startsWith: String(query.name)
}
},
});
let msg = {
hitCount: cars.length,
cars: cars
}
let resp = wm.wm.protobuf.CarSummary.encode(msg);
let end = resp.finish();
let r = res
.header('Server', 'v388 wangan')
.header('Content-Type', 'application/x-protobuf; revision=8053')
.header('Content-Length', end.length.toString())
.status(200);
r.send(Buffer.from(end));
})
// Save upon timeout / exit terminal
app.post('/method/save_terminal_result', async (req, res) => {
// Get the contents from the request
let body = wm.wm.protobuf.SaveTerminalResultRequest.decode(req.body);
// user id is required field
let user = await prisma.user.findFirst({
where: {
id: body.userId
},
});
// Update the car order (NOT IMPLEMENTED)
// Update the completed tutorials
let storedTutorials = user!.tutorials;
body.confirmedTutorials.forEach(
(idx) => storedTutorials[idx] = true
);
await prisma.user.update({
where: {
id: body.userId
},
data: {
tutorials: storedTutorials
}
});
let msg = {
// Success error code
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS,
}
// Encode the save terminal result response
let resp = wm.wm.protobuf.SaveTerminalResultResponse.encode(msg);
let end = resp.finish();
let r = res
.header('Server', 'v388 wangan')
.header('Content-Type', 'application/x-protobuf; revision=8053')
.header('Content-Length', end.length.toString())
.status(200);
r.send(Buffer.from(end));
})
// Terminal scratch (VERY WIP)
app.post('/method/load_scratch_information', async (req, res) => {
// Get the information from the request
let body = wm.wm.protobuf.LoadScratchInformationRequest.decode(req.body);
// Commenting all of the scratch card shit out for now
/*
// Get all of the scratch sheets for the user
let scratchSheets = await prisma.scratchSheet.findMany({
where: {
userId: body.userId
},
include: {
squares: true
}
});
// No scratch sheets for user
if (scratchSheets.length == 0)
{
// Create a new scratch sheet for the user
let sheet = await prisma.scratchSheet.create({
data: {
userId: body.userId
}
})
// Populate each square (with FT ticket for now)
for (let i=0; i<50; i++) {
await prisma.scratchSquare.create({
data: {
sheetId: sheet.id,
category: wm.wm.protobuf.ItemCategory.CAT_RIVAL_MARKER,
itemId: 1,
earned: false
}
});
}
// In future, I will very the way this is populated based on settings
// i.e. totally random items, items based upon real arcade, etc.
// Get the data for the newly created sheet
let newSheet = await prisma.scratchSheet.findFirst({
where: {
userId: body.userId
},
include: {
squares: true
}
});
// Sheet is created successfully
if (newSheet)
{
// Update the scratch sheet list
scratchSheets = [newSheet];
}
}
// Generate the scratch sheet proto
let scratch_sheets : wm.wm.protobuf.ScratchSheet[] = [];
// Loop over all of the protos
for(let sheet of scratchSheets)
{
// Get all of the scratch squares
let scratch_squares : wm.wm.protobuf.ScratchSheet.ScratchSquare[] = [];
// Loop over all of the squares
for (let square of sheet.squares)
{
// Add the current square to the protobuf array
scratch_squares.push(wm.wm.protobuf.ScratchSheet.ScratchSquare.create({
category: square.category,
itemId: square.itemId,
earned: square.earned
}));
}
// Add the scratch sheet to the sheets list
scratch_sheets.push(
wm.wm.protobuf.ScratchSheet.create({
squares: scratch_squares
})
);
}
*/
// Debug: Add all items to the 'acceptable items' list
// Owned user items list
let ownedUserItems : wm.wm.protobuf.UserItem[] = [];
// Get the current date
let date = Date.now();
// If the grant all scratch rewards switch is set, add them to the list
if (Config.getConfig().gameOptions.grantAllScratchRewards)
{
// Grant one of each item every time the menu is loaded
// Loop over all of the window sticker styles
for(let i=1; i<=60; i++)
{
// Add one of each of the window sticker styles to the list
ownedUserItems.push(wm.wm.protobuf.UserItem.create({
category: wm.wm.protobuf.ItemCategory.CAT_WINDOW_DECORATION,
itemId: i,
earnedAt: date,
}));
}
// Loop over all of the rival markers
for(let i=1; i<=80; i++)
{
// Add one of each of the rival markers to the list
ownedUserItems.push(wm.wm.protobuf.UserItem.create({
category: wm.wm.protobuf.ItemCategory.CAT_RIVAL_MARKER,
itemId: i,
earnedAt: date,
}));
}
// Item ID for the scratch cars
let scratchCarIds = [4, 3, 1, 2, 5, 6, 16, 17, 18, 19, 20, 21];
// I literally ripped this from mozilla.org lmfao
let getValueInRange = (min: number, max: number) => {
return Math.random() * (max - min) + min;
}
// If the grant bonus scratch cars switch is set, switch on the value
switch(Config.getConfig().gameOptions.grantBonusScratchCars)
{
case 1: // Grant one of each bonus scratch car (random colour)
// Mini Cooper
scratchCarIds.push(getValueInRange(7, 15));
// S660
scratchCarIds.push(getValueInRange(22, 27));
// S2000
scratchCarIds.push(getValueInRange(28, 36));
// Roadster RF, 280ZT, Leopard
scratchCarIds = scratchCarIds.concat([37, 38, 39]);
break;
case 2: // Grant every colour of each bonus scratch cars
// Mini Cooper
scratchCarIds = scratchCarIds.concat([7, 8, 9, 10, 11, 12, 13, 14, 15]);
// S660
scratchCarIds = scratchCarIds.concat([22, 23, 24, 25, 26, 27]);
// S2000
scratchCarIds = scratchCarIds.concat([28, 29, 30, 31, 32, 33, 34, 35, 36]);
// Roadster RF, 280ZT, Leopard
scratchCarIds = scratchCarIds.concat([37, 38, 39]);
break;
default: // Do not grant bonus scratch cars
break;
}
// Loop over all of the scratch cars
for(let car of scratchCarIds)
{
// Add one of each of the rival markers to the list
ownedUserItems.push(wm.wm.protobuf.UserItem.create({
category: 201, // Scratch Car
itemId: car,
earnedAt: date,
}));
}
}
let msg = {
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS,
scratchSheets: [],
currentSheet: 0,
numOfScratched: 0,
ownedUserItems: ownedUserItems
}
// console.log(scratch_sheets);
let resp = wm.wm.protobuf.LoadScratchInformationResponse.encode(msg);
let end = resp.finish();
let r = res
@ -520,6 +908,99 @@ export default class GameModule extends Module {
r.send(Buffer.from(end));
});
app.post('/method/save_scratch_sheet', async (req, res) => {
// Get the information from the request
let body = wm.wm.protobuf.SaveScratchSheetRequest.decode(req.body);
/*
// Get all of the scratch sheets for the user
let scratchSheets = await prisma.scratchSheet.findMany({
where: {
userId: body.userId
},
include: {
squares: true
}
})
// Get the target scratch sheet
let scratchSheet = scratchSheets[Number(body.targetSheet)];
// Get all of the squares for the scratch sheet
let scratchSquares = await prisma.scratchSquare.findMany({
where: {
sheetId: scratchSheet.id
}
});
// Get the target scratch square
let scratchSquare = scratchSquares[Number(body.targetSquare)];
// Update the revealed scratch square
await prisma.scratchSquare.update({
where: {
id: scratchSquare.id
},
data: {
earned: true
}
});
// Get the number of scratched squares on the page
let numOfScratched = await prisma.scratchSquare.count({
where: {
sheetId: scratchSheet.id,
earned: true
}
})
// Generate the scratch sheet proto
let scratch_sheets : wm.wm.protobuf.ScratchSheet[] = [];
// Loop over all of the protos
for(let sheet of scratchSheets)
{
// Get all of the scratch squares
let scratch_squares : wm.wm.protobuf.ScratchSheet.ScratchSquare[] = [];
// Loop over all of the squares
for (let square of sheet.squares)
{
// Add the current square to the protobuf array
scratch_squares.push(wm.wm.protobuf.ScratchSheet.ScratchSquare.create({
category: square.category,
itemId: square.itemId,
earned: square.earned
}));
}
// Add the scratch sheet to the sheets list
scratch_sheets.push(
wm.wm.protobuf.ScratchSheet.create({
squares: scratch_squares
})
);
}
*/
let msg = {
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS,
scratchSheets : [],
currentSheet: body.targetSheet,
numOfScratched: 0,
}
let resp = wm.wm.protobuf.SaveScratchSheetResponse.encode(msg);
let end = resp.finish();
let r = res
.header('Server', 'v388 wangan')
.header('Content-Type', 'application/x-protobuf; revision=8053')
.header('Content-Length', end.length.toString())
.status(200);
r.send(Buffer.from(end));
})
app.post('/method/update_car', async (req, res) => {
let body = wm.wm.protobuf.UpdateCarRequest.decode(req.body);
let car = await prisma.car.findFirst({
@ -567,15 +1048,23 @@ export default class GameModule extends Module {
})
app.post('/method/create_car', async (req, res) => {
// Get the create car request body
let body = wm.wm.protobuf.CreateCarRequest.decode(req.body);
console.log(body);
// Retrieve user from card chip / user id
let user: User | null;
// User ID provided, use that
if (body.userId) {
user = await prisma.user.findFirst({
where: {
id: body.userId
},
});
} else {
} else { // No user id, use card chip
user = await prisma.user.findFirst({
where: {
chipId: body.cardChipId,
@ -583,30 +1072,75 @@ export default class GameModule extends Module {
},
})
}
// User not found, terminate
if (!user) throw new Error();
// Generate blank car settings object
let settings = await prisma.carSettings.create({
data: {}
});
// Generate blank car state object
let state = await prisma.carState.create({
data: {}
})
let fullTuneUsed = false;
if (body.userItemId) {
// Sets if full tune is used or not
let fullyTuned = false;
// If a user item has been used
if (body.userItemId)
{
console.log(`Item used - ID ${body.userItemId}`);
// Remove the user item from the database
let item = await prisma.userItem.delete({
where: {
dbId: body.userItemId
}
});
console.log(`Item category was ${item.category} and item game ID was ${item.itemId}`);
// If the item used was a full tune ticket
if (item.category == wm.wm.protobuf.ItemCategory.CAT_CAR_TICKET_FREE &&
item.itemId == 5)
{
// This is a full-tune ticket
fullTuneUsed = true;
// Fully tuned is true
fullyTuned = true;
}
console.log('Item deleted!');
}
// User item not used, but car has 740 HP by default
else if (body.car &&
(body.car.tunePower == 17) && (body.car.tuneHandling == 17))
{
// Set fully tuned to be true
fullyTuned = true;
}
// User item not used, but gift cars fully tuned switch is set
else if (Config.getConfig().gameOptions.giftCarsFullyTuned)
{
// List of event / exclusive car IDs
let event_cars = [
0x7A, // Mini
0x82, // S660
0x83, // S2000
0x89, // NDERC
0x8B, // GS130 (Starts at 20 Stories by default)
];
// If the car visual model is not null and is in the list of event cars
if (body.car.visualModel && event_cars.includes(body.car.visualModel))
{
// Set full tune used to be true
fullyTuned = true;
}
}
// Default car values
let carInsert = {
userId: user.id,
manufacturer: body.car.manufacturer!,
@ -622,8 +1156,21 @@ export default class GameModule extends Module {
carStateDbId: state.dbId,
regionId: body.car.regionId!,
};
let additionalInsert = {}
if (fullTuneUsed) {
// Additional car values (for full tune)
let additionalInsert = {
}
// Car is fully tuned
if (fullyTuned) {
// Updated default values
carInsert.level = 8; // C3
carInsert.tunePower = 17; // 740 HP
carInsert.tuneHandling = 17; // 740 HP
// Additional full tune values
additionalInsert = {
stClearBits: 0,
stLoseBits: 0,
@ -632,6 +1179,8 @@ export default class GameModule extends Module {
stConsecutiveWins: 80
};
}
// Insert the car into the database
let car = await prisma.car.create({
data: {
...carInsert,
@ -697,14 +1246,122 @@ export default class GameModule extends Module {
r.send(Buffer.from(end));
});
app.post('/method/load_game_history', (req, res) => {
let msg = {
app.post('/method/load_game_history', async (req, res) => {
// Get the request content
let body = wm.wm.protobuf.LoadGameHistoryRequest.decode(req.body);
// Empty list of time attack records for the player's car
let ta_records : wm.wm.protobuf.LoadGameHistoryResponse.TimeAttackRecord[] = [];
// Get the car info
let car = await prisma.car.findFirst({
where: {
carId: body.carId
}
});
// Get the car's time attack records
let records = await prisma.timeAttackRecord.findMany({
where: {
carId: body.carId
}
});
console.log(records);
// Loop over all of the records
for(let record of records)
{
// This code could probably be done with less DB calls in the future
// Calculate the total rank, total participants for the record
let wholeData = await prisma.timeAttackRecord.findMany({
where: {
course: record.course
},
orderBy: {
time: 'asc'
}
});
// Get the overall number of participants
let wholeParticipants = wholeData.length;
// Whole rank (default: 1)
let wholeRank = 1;
// Loop over all of the participants
for(let row of wholeData)
{
// If the car ID does not match
if (row.carId !== body.carId)
{
// Increment whole rank
wholeRank++;
}
else // Model ID matches
{
// Break the loop
break;
}
}
// Calculate the model rank, model participants for the record
let modelData = await prisma.timeAttackRecord.findMany({
where: {
course: record.course,
model: record.model
},
orderBy: {
time: 'asc'
}
});
// Get the overall number of participants (with the same car model)
let modelParticipants = modelData.length;
// Model rank (default: 1)
let modelRank = 1;
// Loop over all of the participants
for(let row of modelData)
{
// If the car ID does not match
if (row.carId !== body.carId)
{
// Increment whole rank
modelRank++;
}
else // Model ID matches
{
// Break the loop
break;
}
}
// Generate the time attack record object and add it to the list
ta_records.push(wm.wm.protobuf.LoadGameHistoryResponse.TimeAttackRecord.create({
course: record.course,
time: record.time,
tunePower: record.tunePower,
tuneHandling: record.tuneHandling,
wholeParticipants: wholeParticipants,
wholeRank: wholeRank,
modelParticipants: modelParticipants,
modelRank: modelRank
}));
}
let msg = {
error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS,
taRankingUpdatedAt: 0,
taRecords: ta_records,
taRankingUpdatedAt: 1,
ghostBattleCount: 0,
ghostBattleWinCount: 0,
stampSheetCount: 100,
stampSheetCount: 0,
}
let resp = wm.wm.protobuf.LoadGameHistoryResponse.encode(msg);
let end = resp.finish();
let r = res