import { Application } from "express"; import { Module } from "module"; import { prisma } from ".."; import { CarPathandTuning } from "@prisma/client"; import Long from "long"; import { Config } from "../config"; let MersenneTwister = require('chancer'); // Import Proto import * as wm from "../wmmt/wm.proto"; import * as wmsrv from "../wmmt/service.proto"; // Import Util import * as common from "../util/common"; import * as ghost_save_trail from "../util/ghost/ghost_save_trail"; import * as ghost_trail from "../util/ghost/ghost_trail"; import * as ghost_area from "../util/ghost/ghost_area"; export default class GhostModule extends Module { register(app: Application): void { // Load Ghost Battle Info app.post('/method/load_ghost_battle_info', async (req, res) => { // Get the request body for the load stamp target request let body = wm.wm.protobuf.LoadGhostBattleInfoRequest.decode(req.body); let car = await prisma.car.findFirst({ where:{ carId: body.carId }, include:{ gtWing: true, lastPlayedPlace: true } }) // Car History let findChallenger = await prisma.carChallenger.findMany({ where: { challengerCarId: body.carId }, orderBy:{ lastPlayedAt: 'desc' }, take: 10 }) let carsHistory: wm.wm.protobuf.Car[] = []; if(findChallenger.length > 0) { for(let i=0; i 0) { result = -Math.abs(challengers[i].result); } else{ result = Math.abs(challengers[i].result); } carsChallenger.push( wm.wm.protobuf.ChallengerCar.create({ car: carTarget!, stamp: challengers[i].stamp, result: result, area: challengers[i].area }) ); } } // Response data let msg = { error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS, stampTargetCars: carsStamp || null, challengers: carsChallenger || null, stampSheetCount: car!.stampSheetCount, stampSheet: car?.stampSheet || null, stampReturnStats: car?.stampSheet || null, history: carsHistory || null, }; // Encode the response let message = wm.wm.protobuf.LoadGhostBattleInfoResponse.encode(msg); // Send the response to the client common.sendResponse(message, res); }) // Load Stamp Target app.post('/method/load_stamp_target', async (req, res) => { // Get the request body for the load stamp target request let body = wm.wm.protobuf.LoadStampTargetRequest.decode(req.body); let carsStamp: wm.wm.protobuf.StampTargetCar[] = []; let carsChallenger: wm.wm.protobuf.ChallengerCar[] = []; // Get all of the friend cars for the carId provided let stampTargets = await prisma.carStampTarget.findMany({ where: { stampTargetCarId: body.carId, recommended: true }, orderBy:{ locked: 'asc' } }); if(stampTargets) { for(let i=0; i 0) { result = -Math.abs(challengers[i].result); } else{ result = Math.abs(challengers[i].result); } carsChallenger.push( wm.wm.protobuf.ChallengerCar.create({ car: carTarget!, stamp: challengers[i].stamp, result: result, area: challengers[i].area }) ); } } // Response data let msg = { error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS, cars: carsStamp, challengers: carsChallenger }; // Encode the response let message = wm.wm.protobuf.LoadStampTargetResponse.encode(msg); // Send the response to the client common.sendResponse(message, res); }) // Ghost Mode app.post('/method/search_cars_by_level', async (req, res) => { // Get the request body for the search cars by level request let body = wm.wm.protobuf.SearchCarsByLevelRequest.decode(req.body); let car; if(body.regionId !== null && body.regionId !== undefined && body.regionId !== 0) { // Find ghost car by selected level and region ID car = await prisma.car.findMany({ where: { ghostLevel: body.ghostLevel, regionId: body.regionId }, include:{ gtWing: true, lastPlayedPlace: true, } }); } else { // Find ghost car by selected level car = await prisma.car.findMany({ where: { ghostLevel: body.ghostLevel, }, include:{ gtWing: true, lastPlayedPlace: true, } }); } // Randomizing the starting ramp and path based on selected area let rampVal = 0; let pathVal = 0; // Get the ramp and path id let ghost_areas = await ghost_area.GhostArea(body.area); // Set the value rampVal = ghost_areas.rampVal; pathVal = ghost_areas.pathVal; // Empty list of ghost car records let lists_ghostcar: wm.wm.protobuf.GhostCar[] = []; let arr = []; let maxNumber = 0; // If all user car data available is more than 10 for certain level if(car.length > 10) { maxNumber = 10 // Limit to 10 (game default) } // If no more than 10 else { maxNumber = car.length; } // Choose the user's car data randomly to appear while(arr.length < maxNumber) { // Pick random car Id let randomNumber = MersenneTwister.int(0, car.length-1); if(arr.indexOf(randomNumber) === -1) { // Push current number to array arr.push(randomNumber); // Check if ghost trail for certain car is available let ghost_trails = await prisma.ghostTrail.findFirst({ where: { carId: car[randomNumber].carId, area: body.area, crownBattle: false }, orderBy: { playedAt: 'desc' } }); // If regionId is 0 or not set, game will crash after defeating the ghost if(car[randomNumber]!.regionId === 0) { car[randomNumber].regionId = MersenneTwister.int(1, 47); } // Push user's car data without ghost trail if(!(ghost_trails)) { lists_ghostcar.push(wm.wm.protobuf.GhostCar.create({ car: car[randomNumber] })); } // Push user's car data with ghost trail else { // Set the tunePower used when playing certain area car[randomNumber].tunePower = ghost_trails!.tunePower; // Set the tuneHandling used when playing certain area car[randomNumber].tuneHandling = ghost_trails!.tuneHandling; // Push data to Ghost car proto lists_ghostcar.push(wm.wm.protobuf.GhostCar.create({ car: car[randomNumber], nonhuman: false, type: wm.wm.protobuf.GhostType.GHOST_NORMAL, trailId: ghost_trails!.dbId! })); } } } // Check again if car list for that selected region is available of not if(body.regionId !== null && body.regionId !== undefined && body.regionId !== 0) { if(car.length < 1) { // Get current date let date = Math.floor(new Date().getTime() / 1000); let playedPlace = wm.wm.protobuf.Place.create({ placeId: Config.getConfig().placeId, regionId: Config.getConfig().regionId, shopName: Config.getConfig().shopName, country: Config.getConfig().country }); let tunePowerDefault = 0 let tuneHandlingDefault = 0; if(body.ghostLevel === 1) { tunePowerDefault = 1; tuneHandlingDefault = 4; } else if(body.ghostLevel === 2) { tunePowerDefault = 5; tuneHandlingDefault = 5; } else if(body.ghostLevel === 3) { tunePowerDefault = 8; tuneHandlingDefault = 7; } else if(body.ghostLevel === 4) { tunePowerDefault = 10; tuneHandlingDefault = 10; } else if(body.ghostLevel === 5) { tunePowerDefault = 15; tuneHandlingDefault = 10; } else if(body.ghostLevel === 6) { tunePowerDefault = 18; tuneHandlingDefault = 10; } else if(body.ghostLevel === 7) { tunePowerDefault = 20; tuneHandlingDefault = 10; } else if(body.ghostLevel === 8) { tunePowerDefault = 21; tuneHandlingDefault = 10; } else if(body.ghostLevel === 9) { tunePowerDefault = 22; tuneHandlingDefault = 10; } else if(body.ghostLevel === 10) { tunePowerDefault = 24; tuneHandlingDefault = 10; } else if(body.ghostLevel === 11) { tunePowerDefault = 24; tuneHandlingDefault = 24; } // Generate default S660 car data car = wm.wm.protobuf.Car.create({ carId: 999999999, // Don't change this name: 'S660', regionId: body.regionId, // IDN (福井) manufacturer: 12, // HONDA model: 105, // S660 [JW5] visualModel: 130, // S660 [JW5] defaultColor: 0, customColor: 0, wheel: 20, wheelColor: 0, aero: 0, bonnet: 0, wing: 0, mirror: 0, neon: 0, trunk: 0, plate: 0, plateColor: 0, plateNumber: 0, tunePower: tunePowerDefault, tuneHandling: tuneHandlingDefault, rivalMarker: 32, aura: 551, windowSticker: true, windowStickerString: 'BAYSHORE', windowStickerFont: 0, title: 'No Ghost for this Region', level: 65, // SSSSS lastPlayedAt: date, country: 'JPN', lastPlayedPlace: playedPlace }); // Push data to Ghost car proto lists_ghostcar.push(wm.wm.protobuf.GhostCar.create({ car: car, nonhuman: true, type: wm.wm.protobuf.GhostType.GHOST_DEFAULT, })); } // else{} have car list } // Response data let msg = { error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS, ramp: rampVal, path: pathVal, ghosts: lists_ghostcar, selectionMethod: Number(wm.wm.protobuf.GhostSelectionMethod.GHOST_SELECT_BY_LEVEL) }; // Encode the response let message = wm.wm.protobuf.SearchCarsByLevelResponse.encode(msg); // Send the response to the client common.sendResponse(message, res); }) // Load Normal Ghost Trail data app.post('/method/load_ghost_drive_data', async (req, res) => { // Get the request body for the load ghost drive data request let body = wm.wm.protobuf.LoadGhostDriveDataRequest.decode(req.body); // Get the path value from request body let path = body.path; // Empty list of ghost drive data records let lists_ghostcar: wm.wm.protobuf.LoadGhostDriveDataResponse.GhostDriveData[] = []; // Check how many opponent ghost data available (including user data) for(let i=0; i { // Get the request body for the register ghost trail request let body = wm.wm.protobuf.RegisterGhostTrailRequest.decode(req.body); // Get the session id let actualSessionId: number = 0; // If the session are set, and are long data if(Long.isLong(body.ghostSessionId)) { // Convert them to BigInt and add to the data actualSessionId = common.getBigIntFromLong(body.ghostSessionId); } // OCM game mode session id if(actualSessionId > 100 && actualSessionId < 201) { console.log('OCM Ghost Battle Game found'); // User playing ocm battle game mode await ghost_save_trail.saveOCMGhostTrail(body); } // Ghost Battle or Crown Ghost Battle game Mode session id else if(actualSessionId > 0 && actualSessionId < 101) { // Check if it is crown ghost battle or not (crown ghost battle don't have time, driveData, trendBinaryByArea, trendBinaryByCar, trendBinaryByUser value from request body) if(!(body.trendBinaryByArea) && !(body.trendBinaryByCar) && !(body.trendBinaryByUser)) { console.log('Crown Ghost Battle Game found'); // User is playing crown ghost battle game mode await ghost_save_trail.saveCrownGhostTrail(body); } else { console.log('Normal Ghost Battle Game found'); // User is playing normal ghost battle game mode await ghost_save_trail.saveNormalGhostTrail(body); // Saving Ghost Path and Tuning await ghost_save_trail.savePathAndTuning(body); } } // Response data let msg = { error: wm.wm.protobuf.ErrorCode.ERR_SUCCESS } // Encode the response let message = wm.wm.protobuf.RegisterGhostTrailResponse.encode(msg); // Send the response to the client common.sendResponse(message, res); }) // Load Crown Ghost Trail or Top 1 OCM Ghost Trail app.get('/resource/ghost_trail', async (req, res) => { // Get url query parameter [For Crown Ghost Battle] let pCarId = Number(req.query.car_id); let pArea = Number(req.query.area); // Get url query parameter [For OCM Ghost Battle] let pTrailId = Number(req.query.trail_id) || undefined; // Declare variable let rampVal: number = 0; let pathVal: number = 0; let playedAt: number = 0; let ghostTrail; // Query parameter from OCM Battle available if(pTrailId) { console.log('Requesting OCM Ghost Trail'); // Get the trail data let ghost_trails = await ghost_trail.getOCMGhostTrail(pCarId, pTrailId); pArea = ghost_trails.areaVal; rampVal = ghost_trails.rampVal; pathVal = ghost_trails.pathVal; playedAt = ghost_trails.playedAt; ghostTrail = ghost_trails.ghostTrail; } // Query parameter from Crown Ghost Battle available else { console.log('Requesting Crown Ghost Trail'); // Get the crown trail data let ghost_trails = await ghost_trail.getCrownGhostTrail(pCarId, pArea); rampVal = ghost_trails.rampVal; pathVal = ghost_trails.pathVal; playedAt = ghost_trails.playedAt; ghostTrail = ghost_trails.ghostTrail; } // Response data let msg = { carId: pCarId, area: pArea, ramp: rampVal, path: pathVal, playedAt: playedAt, trail: ghostTrail }; // Encode the response let message = wm.wm.protobuf.GhostTrail.encode(msg); // Send the response to the client common.sendResponse(message, res); }) // Load ghost path and tunings per area app.post('/method/load_paths_and_tunings', async (req, res) => { // Get the request body for the load path and tunings request let body = wm.wm.protobuf.LoadPathsAndTuningsRequest.decode(req.body); // Empty list of car path and tuning records let carTbyP: wm.wm.protobuf.LoadPathsAndTuningsResponse.CarTuningsByPath[] = []; // Loop GID_RUNAREA_* for(let j=0; j<19; j++) { // 14 - 16 are dummy area, 17 is C1 Closed if(j >= 14){ j = 18; // GID_RUNAREA_HIROSHIMA } let rampVal = 0; let pathVal = 0; // Get the ramp and path id let ghost_areas = await ghost_area.GhostArea(j); // Set the value rampVal = ghost_areas.rampVal; pathVal = ghost_areas.pathVal; // Empty list of car tuning records let carTuning: wm.wm.protobuf.CarTuning[] = []; let pathAndTuning: CarPathandTuning | null; // Loop to how many opponent ghost selected by user for(let i=0; i { // Get the information from the request let body = wmsrv.wm.protobuf.LockCrownRequest.decode(req.body); // Response data let msg = { error: wmsrv.wm.protobuf.ErrorCode.ERR_SUCCESS, }; // Encode the response let message = wmsrv.wm.protobuf.LockCrownResponse.encode(msg); // Send the response to the client common.sendResponse(message, res); }) } }