From 398bfa209239e372db66759b1a942145e963f925 Mon Sep 17 00:00:00 2001 From: Dillon Skaggs Date: Tue, 6 Jul 2021 14:02:55 -0700 Subject: [PATCH] feat(): add animation and call service & controllers (#259) * feat(): add animation and call service & controllers - run animations on interval so we don't lose animations when walking through doors - move cl_call.ts to a class * fix(animation/service): make everything private except for open/closePhone and start/endPhoneCall --- phone/src/locale/en.json | 1 + .../client/animations/animation.controller.ts | 3 + .../client/animations/animation.service.ts | 194 ++++++++++++++++++ resources/client/calls/cl_calls.controller.ts | 65 ++++++ resources/client/calls/cl_calls.service.ts | 108 ++++++++++ resources/client/cl_call.ts | 141 ------------- resources/client/cl_main.ts | 10 +- resources/client/client.ts | 3 +- resources/client/functions.ts | 130 +----------- resources/package.json | 4 +- resources/server/calls/calls.controller.ts | 6 +- resources/server/calls/calls.service.ts | 24 +-- resources/yarn.lock | 18 +- typings/call.ts | 5 + 14 files changed, 413 insertions(+), 299 deletions(-) create mode 100644 resources/client/animations/animation.controller.ts create mode 100644 resources/client/animations/animation.service.ts create mode 100644 resources/client/calls/cl_calls.controller.ts create mode 100644 resources/client/calls/cl_calls.service.ts delete mode 100644 resources/client/cl_call.ts diff --git a/phone/src/locale/en.json b/phone/src/locale/en.json index 2ae74754d..828df51a4 100644 --- a/phone/src/locale/en.json +++ b/phone/src/locale/en.json @@ -78,6 +78,7 @@ "APPS_DIALER_NAVBAR_HISTORY": "History", "APPS_DIALER_NAVBAR_DIAL": "Dial", "APPS_DIALER_NAVBAR_CONTACTS": "Contacts", + "APPS_DIALER_BUSY_LINE": "The line was busy.", "APPS_TWITTER_TWEET": "Tweet", "APPS_TWITTER_TWEET_MESSAGE_PLACEHOLDER": "What's happening?", diff --git a/resources/client/animations/animation.controller.ts b/resources/client/animations/animation.controller.ts new file mode 100644 index 000000000..e4325aea3 --- /dev/null +++ b/resources/client/animations/animation.controller.ts @@ -0,0 +1,3 @@ +import { AnimationService } from "./animation.service"; + +export const animationService = new AnimationService(); \ No newline at end of file diff --git a/resources/client/animations/animation.service.ts b/resources/client/animations/animation.service.ts new file mode 100644 index 000000000..f814fdcf1 --- /dev/null +++ b/resources/client/animations/animation.service.ts @@ -0,0 +1,194 @@ +import { newPhoneProp, removePhoneProp } from '../functions'; +import { Delay } from '../../utils/fivem'; + +export enum AnimationState { + ON_CALL, + PHONE_OPEN, + ON_CAMERA, +} + +export class AnimationService { + private animationInterval: NodeJS.Timeout; + private onCall: boolean = false; + private phoneOpen: boolean = false; + private onCamera: boolean = false; + + private createAnimationInterval() { + this.animationInterval = setInterval(async () => { + const playerPed = PlayerPedId(); + if (this.onCall) { + this.handleCallAnimation(playerPed); + } else if (this.phoneOpen && !this.onCamera) { + this.handleOpenAnimation(playerPed); + } + }, 250); + } + + private setPhoneState(state: AnimationState, stateValue: boolean) { + switch (state) { + case AnimationState.ON_CALL: + this.onCall = stateValue; + break; + case AnimationState.PHONE_OPEN: + this.phoneOpen = stateValue; + break; + case AnimationState.ON_CAMERA: + this.onCamera = stateValue; + break; + } + + if (!this.onCall && !this.phoneOpen) { + if (this.animationInterval) { + clearInterval(this.animationInterval); + this.animationInterval = null; + } + } else if (!this.animationInterval) { + this.createAnimationInterval(); + } + } + + private handleCallAnimation(playerPed: number) { + if (IsPedInAnyVehicle(playerPed, true)) { + this.handleOnCallInVehicle(playerPed); + } else { + this.handleOnCallNormal(playerPed); + } + } + + private handleOpenAnimation(playerPed: number) { + if (IsPedInAnyVehicle(playerPed, true)) { + this.handleOpenVehicleAnim(playerPed); + } else { + this.handleOpenNormalAnim(playerPed); + } + } + + private handleCallEndAnimation(playerPed: number) { + if (IsPedInAnyVehicle(playerPed, true)) { + this.handleCallEndVehicleAnim(playerPed); + } else { + this.handleCallEndNormalAnim(playerPed); + } + } + + private handleCloseAnimation(playerPed: number) { + if (IsPedInAnyVehicle(playerPed, true)) { + this.handleCloseVehicleAnim(playerPed); + } else { + this.handleCloseNormalAnim(playerPed); + } + } + + openPhone(): void { + newPhoneProp(); + if (!this.onCall) { + this.handleOpenAnimation(PlayerPedId()); + } + this.setPhoneState(AnimationState.PHONE_OPEN, true); + } + + closePhone(): void { + removePhoneProp(); + this.setPhoneState(AnimationState.PHONE_OPEN, false); + if (!this.onCall) { + this.handleCloseAnimation(PlayerPedId()); + } + } + + async startPhoneCall(): Promise { + this.handleCallAnimation(PlayerPedId()); + this.setPhoneState(AnimationState.ON_CALL, true); + } + + async endPhoneCall(): Promise { + this.handleCallEndAnimation(PlayerPedId()); + this.setPhoneState(AnimationState.ON_CALL, false); + } + + private async loadAnimDict(dict: any) { + //-- Loads the animation dict. Used in the anim functions. + RequestAnimDict(dict); + while (!HasAnimDictLoaded(dict)) { + await Delay(100); + } + } + + private async handleOpenVehicleAnim(playerPed: number): Promise { + const dict = 'anim@cellphone@in_car@ps'; + const anim = 'cellphone_text_in'; + await this.loadAnimDict(dict); + + if (!IsEntityPlayingAnim(playerPed, dict, anim, 3)) { + SetCurrentPedWeapon(playerPed, 0xa2719263, true); + TaskPlayAnim(playerPed, dict, anim, 7.0, -1, -1, 50, 0, false, false, false); + } + } + + private async handleOpenNormalAnim(playerPed: number): Promise { + //While not in a vehicle it will use this dict. + const dict = 'cellphone@'; + const anim = 'cellphone_text_in'; + await this.loadAnimDict(dict); + + if (!IsEntityPlayingAnim(playerPed, dict, anim, 3)) { + SetCurrentPedWeapon(playerPed, 0xa2719263, true); + TaskPlayAnim(playerPed, dict, anim, 8.0, -1, -1, 50, 0, false, false, false); + } + } + + private async handleCloseVehicleAnim(playerPed: number): Promise { + const DICT = 'anim@cellphone@in_car@ps'; + StopAnimTask(playerPed, DICT, 'cellphone_text_in', 1.0); // Do both incase they were on the phone. + StopAnimTask(playerPed, DICT, 'cellphone_call_to_text', 1.0); + removePhoneProp(); + } + + private async handleCloseNormalAnim(playerPed: number): Promise { + const DICT = 'cellphone@'; + const ANIM = 'cellphone_text_out'; + StopAnimTask(playerPed, DICT, 'cellphone_text_in', 1.0); + await Delay(100); + await this.loadAnimDict(DICT); + TaskPlayAnim(playerPed, DICT, ANIM, 7.0, -1, -1, 50, 0, false, false, false); + await Delay(200); + StopAnimTask(playerPed, DICT, ANIM, 1.0); + removePhoneProp(); + } + + private async handleOnCallInVehicle(playerPed: number): Promise { + const DICT = 'anim@cellphone@in_car@ps'; + const ANIM = 'cellphone_call_listen_base'; + + if (!IsEntityPlayingAnim(playerPed, DICT, ANIM, 3)) { + await this.loadAnimDict(DICT); + TaskPlayAnim(playerPed, DICT, ANIM, 3.0, 3.0, -1, 49, 0, false, false, false); + } + } + + private async handleOnCallNormal(playerPed: number): Promise { + const DICT = 'cellphone@'; + const ANIM = 'cellphone_call_listen_base'; + if (!IsEntityPlayingAnim(playerPed, DICT, ANIM, 3)) { + await this.loadAnimDict(DICT); + TaskPlayAnim(playerPed, DICT, ANIM, 3.0, 3.0, -1, 49, 0, false, false, false); + } + } + + private async handleCallEndVehicleAnim(playerPed: number): Promise { + const DICT = 'anim@cellphone@in_car@ps'; + const ANIM = 'cellphone_call_to_text'; + StopAnimTask(playerPed, DICT, 'cellphone_call_listen_base', 1.0); + await this.loadAnimDict(DICT); + TaskPlayAnim(playerPed, DICT, ANIM, 1.3, 5.0, -1, 50, 0, false, false, false); + } + + private async handleCallEndNormalAnim(playerPed: number): Promise { + const DICT = 'cellphone@'; + const ANIM = 'cellphone_call_to_text'; + + if (IsEntityPlayingAnim(playerPed, 'cellphone@', 'cellphone_call_listen_base', 49)) { + await this.loadAnimDict(DICT); + TaskPlayAnim(playerPed, DICT, ANIM, 2.5, 8.0, -1, 50, 0, false, false, false); + } + }; +} diff --git a/resources/client/calls/cl_calls.controller.ts b/resources/client/calls/cl_calls.controller.ts new file mode 100644 index 000000000..a9904c1ca --- /dev/null +++ b/resources/client/calls/cl_calls.controller.ts @@ -0,0 +1,65 @@ +import { CallEvents } from '../../../typings/call'; +import { CallHistoryItem } from '../../../typings/call'; +import { IAlertProps } from '../../../typings/alerts'; +import { CallService } from './cl_calls.service'; +import { animationService } from '../animations/animation.controller'; + +const callService = new CallService(); + +RegisterNuiCallbackType(CallEvents.INITIALIZE_CALL); // Fires when the call is started. +on(`__cfx_nui:${CallEvents.INITIALIZE_CALL}`, (data: any, cb: Function) => { + emitNet(CallEvents.INITIALIZE_CALL, data.number); + cb(); +}); + +onNet(CallEvents.START_CALL, (transmitter: string, receiver: string, isTransmitter: boolean) => { + if(isTransmitter){ + animationService.startPhoneCall(); + } + callService.handleStartCall(transmitter, receiver, isTransmitter) +}); + +RegisterNuiCallbackType(CallEvents.ACCEPT_CALL); // Fires when the TARGET accepts. +on(`__cfx_nui:${CallEvents.ACCEPT_CALL}`, (data: any, cb: Function) => { + animationService.startPhoneCall(); + emitNet(CallEvents.ACCEPT_CALL, data.transmitterNumber); + cb(); +}); + +onNet(CallEvents.WAS_ACCEPTED, (channelId: number, currentCall: CallHistoryItem, isTransmitter: boolean) => { + callService.handleCallAccepted(channelId, currentCall, isTransmitter) +}); + +RegisterNuiCallbackType(CallEvents.REJECTED); // Fires when cancelling and rejecting a call. +on(`__cfx_nui:${CallEvents.REJECTED}`, (data: any, cb: Function) => { + console.log('rejected?') + emitNet(CallEvents.REJECTED, data.phoneNumber); + cb(); +}); + +onNet(CallEvents.WAS_REJECTED, async() => { + callService.handleRejectCall(); + animationService.endPhoneCall(); +}); + +RegisterNuiCallbackType(CallEvents.END_CALL); // Fires when ending an ACTIVE call +on(`__cfx_nui:${CallEvents.END_CALL}`, (data: any, cb: Function) => { + const end = Date.now(); + animationService.endPhoneCall(); + emitNet(CallEvents.END_CALL, data.transmitterNumber, end); + cb(); +}); + +onNet(CallEvents.WAS_ENDED, () => { + callService.handleEndCall(); + animationService.endPhoneCall(); +}); + + +onNet(CallEvents.FETCH_CALLS, (calls: CallHistoryItem[]) => { + callService.handleFetchCalls(calls); +}); + +onNet(CallEvents.SEND_ALERT, (alert: IAlertProps) => { + callService.handleSendAlert(alert); +}); diff --git a/resources/client/calls/cl_calls.service.ts b/resources/client/calls/cl_calls.service.ts new file mode 100644 index 000000000..12975ad48 --- /dev/null +++ b/resources/client/calls/cl_calls.service.ts @@ -0,0 +1,108 @@ +import { IAlertProps } from "../../../typings/alerts"; +import { CallEvents, CallHistoryItem, CallRejectReasons } from "../../../typings/call"; + +const exp = (global as any).exports; + +export class CallService { + private currentCall: number = 0; + + + isInCall() { + return this.currentCall !== 0; + } + + openCallModal(show: boolean) { + SendNUIMessage({ + app: 'CALL', + method: CallEvents.SET_CALL_MODAL, + data: show, + }); + } + + handleRejectCall() { + // we don't want to reset our UI if we're in a call already. + if (this.isInCall()) return; + this.openCallModal(false); + SendNUIMessage({ + app: 'CALL', + method: CallEvents.SET_CALLER, + data: { + transmitter: null, + receiver: null, + isTransmitter: null, + accepted: false, + active: false, + }, + }); + } + + handleStartCall(transmitter: string, receiver: string, isTransmitter: boolean) { + // If we're already in a call we want to automatically reject + if (this.isInCall()) return emitNet(CallEvents.REJECTED, transmitter, CallRejectReasons.BUSY_LINE); + this.openCallModal(true); + + SendNUIMessage({ + app: 'CALL', + method: CallEvents.SET_CALLER, + data: { + active: true, + transmitter: transmitter, + receiver: receiver, + isTransmitter: isTransmitter, + accepted: false, + }, + }); + } + + handleCallAccepted(channelId: number, currentCall: CallHistoryItem, isTransmitter: boolean) { + this.currentCall = channelId + exp['pma-voice'].setCallChannel(channelId); + // phoneCallStartAnim(); // Trigger call animation only if the call was accepted. + SendNUIMessage({ + app: 'CALL', + method: CallEvents.SET_CALLER, + data: { + active: true, + transmitter: currentCall.transmitter, + receiver: currentCall.receiver, + isTransmitter: isTransmitter, + accepted: true, + }, + }); + } + + handleEndCall() { + this.currentCall = 0 + exp['pma-voice'].setCallChannel(0); + this.openCallModal(false); + + SendNUIMessage({ + app: 'CALL', + method: CallEvents.SET_CALLER, + data: { + transmitter: null, + receiver: null, + isTransmitter: null, + accepted: false, + active: false, + }, + }) + } + + handleSendAlert(alert: IAlertProps) { + SendNUIMessage({ + app: 'DIALER', + method: CallEvents.SEND_ALERT, + data: alert, + }); + } + + handleFetchCalls(calls: CallHistoryItem[]) { + SendNUIMessage({ + app: 'DIALER', + method: CallEvents.SET_CALL_HISTORY, + data: calls, + }); + } + +} diff --git a/resources/client/cl_call.ts b/resources/client/cl_call.ts deleted file mode 100644 index cd5df4ca5..000000000 --- a/resources/client/cl_call.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { CallEvents } from '../../typings/call'; -import { CallHistoryItem } from '../../typings/call'; -import { phoneCallStartAnim, phoneCallEndAnim } from './functions'; -import { IAlertProps } from '../../typings/alerts'; - -const exp = (global as any).exports; - -RegisterNuiCallbackType(CallEvents.INITIALIZE_CALL); // Fires when the call is started. -on(`__cfx_nui:${CallEvents.INITIALIZE_CALL}`, (data: any, cb: Function) => { - emitNet(CallEvents.INITIALIZE_CALL, data.number); - cb(); -}); - -onNet(CallEvents.START_CALL, (transmitter: string, receiver: string, isTransmitter: boolean) => { - openCallModal(true); - - SendNuiMessage( - JSON.stringify({ - app: 'CALL', - method: CallEvents.SET_CALLER, - data: { - active: true, - transmitter: transmitter, - receiver: receiver, - isTransmitter: isTransmitter, - accepted: false, - }, - }), - ); -}); - -RegisterNuiCallbackType(CallEvents.ACCEPT_CALL); // Fires when the TARGET accepts. -on(`__cfx_nui:${CallEvents.ACCEPT_CALL}`, (data: any, cb: Function) => { - emitNet(CallEvents.ACCEPT_CALL, data.transmitterNumber); - cb(); -}); - -onNet( - CallEvents.WAS_ACCEPTED, - (channelId: number, currentCall: CallHistoryItem, isTransmitter: boolean) => { - exp['pma-voice'].setCallChannel(channelId); - phoneCallStartAnim(); // Trigger call animation only if the call was accepted. - SendNuiMessage( - JSON.stringify({ - app: 'CALL', - method: CallEvents.SET_CALLER, - data: { - active: true, - transmitter: currentCall.transmitter, - receiver: currentCall.receiver, - isTransmitter: isTransmitter, - accepted: true, - }, - }), - ); - }, -); - -RegisterNuiCallbackType(CallEvents.REJECTED); // Fires when cancelling and rejecting a call. -on(`__cfx_nui:${CallEvents.REJECTED}`, (data: any, cb: Function) => { - emitNet(CallEvents.REJECTED, data.phoneNumber); - cb(); -}); - -onNet(CallEvents.WAS_REJECTED, () => { - openCallModal(false); - SendNuiMessage( - JSON.stringify({ - app: 'CALL', - method: CallEvents.SET_CALLER, - data: { - transmitter: null, - receiver: null, - isTransmitter: null, - accepted: false, - active: false, - }, - }), - ); -}); - -onNet(CallEvents.SEND_HANGUP_ANIM, () => { - phoneCallEndAnim(); -}); - -RegisterNuiCallbackType(CallEvents.END_CALL); // Fires when ending an ACTIVE call -on(`__cfx_nui:${CallEvents.END_CALL}`, (data: any, cb: Function) => { - const end = Date.now(); - emitNet(CallEvents.END_CALL, data.transmitterNumber, end); - cb(); -}); - -onNet(CallEvents.WAS_ENDED, () => { - exp['pma-voice'].setCallChannel(0); - openCallModal(false); - - SendNuiMessage( - JSON.stringify({ - app: 'CALL', - method: CallEvents.SET_CALLER, - data: { - transmitter: null, - receiver: null, - isTransmitter: null, - accepted: false, - active: false, - }, - }), - ); -}); - -function openCallModal(show: boolean) { - SendNuiMessage( - JSON.stringify({ - app: 'CALL', - method: CallEvents.SET_CALL_MODAL, - data: show, - }), - ); -} - -onNet(CallEvents.FETCH_CALLS, (calls: CallHistoryItem[]) => { - SendNuiMessage( - JSON.stringify({ - app: 'DIALER', - method: CallEvents.SET_CALL_HISTORY, - data: calls, - }), - ); -}); - -onNet(CallEvents.SEND_ALERT, (alert: IAlertProps) => { - console.log(alert); - SendNuiMessage( - JSON.stringify({ - app: 'DIALER', - method: CallEvents.SEND_ALERT, - data: alert, - }), - ); -}); diff --git a/resources/client/cl_main.ts b/resources/client/cl_main.ts index 5105ea981..5d0938376 100644 --- a/resources/client/cl_main.ts +++ b/resources/client/cl_main.ts @@ -1,13 +1,12 @@ import { sendMessage } from '../utils/messages'; import { PhoneEvents } from '../../typings/phone'; import { TwitterEvents } from '../../typings/twitter'; -import { phoneCloseAnim, phoneOpenAnim, removePhoneProp } from './functions'; import { MessageEvents } from '../../typings/messages'; import { BankEvents } from '../../typings/bank'; import { PhotoEvents } from '../../typings/photo'; import { CallEvents } from '../../typings/call'; import { config } from './client'; - +import { animationService } from './animations/animation.controller'; let isPhoneOpen = false; /* * * * * * * * * * * * * @@ -50,7 +49,7 @@ const getCurrentGameTime = () => { const showPhone = async (): Promise => { isPhoneOpen = true; const time = getCurrentGameTime(); - await phoneOpenAnim(); // Animation starts before the phone is open + await animationService.openPhone(); // Animation starts before the phone is open emitNet('phone:getCredentials'); SetCursorLocation(0.9, 0.922); //Experimental sendMessage('PHONE', PhoneEvents.SET_VISIBILITY, true); @@ -63,7 +62,7 @@ const showPhone = async (): Promise => { const hidePhone = async (): Promise => { isPhoneOpen = false; sendMessage('PHONE', PhoneEvents.SET_VISIBILITY, false); - await phoneCloseAnim(); + await animationService.closePhone(); SetNuiFocus(false, false); SetNuiFocusKeepInput(false); emit('npwd:disableControlActions', false); @@ -120,7 +119,8 @@ AddEventHandler('onResourceStop', function (resource: string) { if (resource === GetCurrentResourceName()) { sendMessage('PHONE', PhoneEvents.SET_VISIBILITY, false); SetNuiFocus(false, false); - removePhoneProp(); //Deletes the phone incase it was attached. + animationService.endPhoneCall(); + animationService.closePhone(); ClearPedTasks(PlayerPedId()); //Leave here until launch as it'll fix any stuck animations. } }); diff --git a/resources/client/client.ts b/resources/client/client.ts index b71a071c6..7fe5618f3 100644 --- a/resources/client/client.ts +++ b/resources/client/client.ts @@ -6,6 +6,7 @@ export const config: ResourceConfig = JSON.parse( LoadResourceFile(GetCurrentResourceName(), 'config.json'), ); + import './cl_main'; import './cl_twitter'; import './cl_contacts'; @@ -14,7 +15,7 @@ import './cl_bank'; import './cl_notes'; import './cl_photo'; import './cl_messages'; -import './cl_call'; +import './calls/cl_calls.controller'; import './cl_match'; import './functions'; diff --git a/resources/client/functions.ts b/resources/client/functions.ts index 03f1c72a8..0c8d05206 100644 --- a/resources/client/functions.ts +++ b/resources/client/functions.ts @@ -10,7 +10,9 @@ const phoneModel = 'prop_amb_phone'; * * * * * * * * * * * * * */ -const newPhoneProp = async () => { +// TODO: add a option to make server side for people who use entity lockdown. + +export const newPhoneProp = async () => { removePhoneProp(); //deletes the already existing prop before creating another. if (!propCreated) { RequestModel(phoneModel); @@ -50,132 +52,8 @@ const newPhoneProp = async () => { export function removePhoneProp() { //-- Triggered in newphoneProp function. Only way to destory the prop correctly. if (prop != 0) { - //Citizen.invokeNative(0xAE3CBE5BF394C9C9 , Citizen.pointerValueIntInitialized(prop)) DeleteEntity(prop); prop = 0; propCreated = false; } -} - -/* * * * * * * * * * * * * - * - * Animations - * - * * * * * * * * * * * * */ - -export async function loadAnimDict(dict: any) { - //-- Loads the animation dict. Used in the anim functions. - while (!HasAnimDictLoaded(dict)) { - RequestAnimDict(dict); - await Delay(100); - } -} - -const handleOpenVehicleAnim = async (playerPed: number): Promise => { - const dict = 'anim@cellphone@in_car@ps'; - - SetCurrentPedWeapon(playerPed, 0xa2719263, true); - ClearPedTasks(playerPed); - await loadAnimDict(dict); - TaskPlayAnim(playerPed, dict, 'cellphone_text_in', 7.0, -1, -1, 50, 0, false, false, false); - await Delay(300); - await newPhoneProp(); -}; - -const handleOpenNormalAnim = async (playerPed: number): Promise => { - //While not in a vehicle it will use this dict. - const dict = 'cellphone@'; - - SetCurrentPedWeapon(playerPed, 0xa2719263, true); - ClearPedTasks(playerPed); - await loadAnimDict(dict); - TaskPlayAnim(playerPed, dict, 'cellphone_text_in', 8.0, -1, -1, 50, 0, false, false, false); - await Delay(300); - await newPhoneProp(); -}; - -const handleCloseVehicleAnim = async (playerPed: number): Promise => { - const DICT = 'anim@cellphone@in_car@ps'; - StopAnimTask(playerPed, DICT, 'cellphone_text_in', 1.0); // Do both incase they were on the phone. - StopAnimTask(playerPed, DICT, 'cellphone_call_to_text', 1.0); - removePhoneProp(); -}; - -const handleCloseNormalAnim = async (playerPed: number): Promise => { - const DICT = 'cellphone@'; - const ANIM = 'cellphone_text_out'; - StopAnimTask(playerPed, DICT, 'cellphone_text_in', 1.0); - await Delay(100); - await loadAnimDict(DICT); - TaskPlayAnim(playerPed, DICT, ANIM, 7.0, -1, -1, 50, 0, false, false, false); - await Delay(200); - StopAnimTask(playerPed, DICT, ANIM, 1.0); - removePhoneProp(); -}; - -const handleCallStartVehicleAnim = async (playerPed: number): Promise => { - const DICT = 'anim@cellphone@in_car@ps'; - const ANIM = 'cellphone_call_listen_base'; - - StopAnimTask(playerPed, DICT, 'cellphone_text_in', 1.0); - await loadAnimDict(DICT); - TaskPlayAnim(playerPed, DICT, ANIM, 3.0, 3.0, -1, 49, 0, false, false, false); -}; - -const handleCallEndVehicleAnim = async (playerPed: number): Promise => { - const DICT = 'anim@cellphone@in_car@ps'; - const ANIM = 'cellphone_call_to_text'; - StopAnimTask(playerPed, DICT, 'cellphone_call_listen_base', 1.0); - await loadAnimDict(DICT); - TaskPlayAnim(playerPed, DICT, ANIM, 1.3, 5.0, -1, 50, 0, false, false, false); -}; - -const handleCallStartNormalAnim = async (playerPed: number): Promise => { - const DICT = 'cellphone@'; - const ANIM = 'cellphone_call_listen_base'; - await loadAnimDict(DICT); - TaskPlayAnim(playerPed, DICT, ANIM, 3.0, 3.0, -1, 49, 0, false, false, false); -}; - -const handleCallEndNormalAnim = async (playerPed: number): Promise => { - const DICT = 'cellphone@'; - const ANIM = 'cellphone_call_to_text'; - - if (IsEntityPlayingAnim(playerPed, 'cellphone@', 'cellphone_call_listen_base', 49)) { - await loadAnimDict(DICT); - TaskPlayAnim(playerPed, DICT, ANIM, 2.5, 8.0, -1, 50, 0, false, false, false); - } -}; - -export async function phoneOpenAnim(): Promise { - const playerPed = PlayerPedId(); - removePhoneProp(); - if (IsPedInAnyVehicle(playerPed, true)) { - return await handleOpenVehicleAnim(playerPed); - } - return await handleOpenNormalAnim(playerPed); -} - -export async function phoneCloseAnim() { - const playerPed = PlayerPedId(); - if (IsPedInAnyVehicle(playerPed, true)) { - return await handleCloseVehicleAnim(playerPed); - } - await handleCloseNormalAnim(playerPed); -} - -export async function phoneCallStartAnim(): Promise { - const playerPed = PlayerPedId(); - if (IsPedInAnyVehicle(playerPed, true)) { - return await handleCallStartVehicleAnim(playerPed); - } - return await handleCallStartNormalAnim(playerPed); -} - -export async function phoneCallEndAnim(): Promise { - const playerPed = PlayerPedId(); - if (IsPedInAnyVehicle(playerPed, true)) { - return await handleCallEndVehicleAnim(playerPed); - } - return await handleCallEndNormalAnim(playerPed); -} +} \ No newline at end of file diff --git a/resources/package.json b/resources/package.json index f9c6de3f3..26102bd8a 100644 --- a/resources/package.json +++ b/resources/package.json @@ -29,8 +29,8 @@ "@babel/core": "^7.12.16", "@babel/preset-env": "^7.12.1", "@babel/preset-typescript": "^7.12.1", - "@citizenfx/client": "^2.0.4035-1", - "@citizenfx/server": "^2.0.4035-1", + "@citizenfx/client": "^2.0.4170-1", + "@citizenfx/server": "^2.0.4167-1", "@types/jest": "^26.0.14", "@types/md5": "^2.2.1", "@types/node": "^14.11.10", diff --git a/resources/server/calls/calls.controller.ts b/resources/server/calls/calls.controller.ts index 9196f6a5e..e4346610d 100644 --- a/resources/server/calls/calls.controller.ts +++ b/resources/server/calls/calls.controller.ts @@ -1,4 +1,4 @@ -import { CallEvents } from '../../../typings/call'; +import { CallEvents, CallRejectReasons } from '../../../typings/call'; import { getSource } from '../utils/miscUtils'; import CallService from './calls.service'; import { callLogger } from './calls.utils'; @@ -21,9 +21,9 @@ onNet(CallEvents.ACCEPT_CALL, (transmitterNumber: string) => { ); }); -onNet(CallEvents.REJECTED, (transmitterNumber: string) => { +onNet(CallEvents.REJECTED, (transmitterNumber: string, reason?: CallRejectReasons) => { const src = getSource(); - CallService.handleRejectCall(src, transmitterNumber).catch((e) => + CallService.handleRejectCall(src, transmitterNumber, reason).catch((e) => callLogger.error( `Error occured in rejectcall event (${transmitterNumber}), Error: ${e.message}`, ), diff --git a/resources/server/calls/calls.service.ts b/resources/server/calls/calls.service.ts index b81bbf7a3..5e4f39000 100644 --- a/resources/server/calls/calls.service.ts +++ b/resources/server/calls/calls.service.ts @@ -1,5 +1,5 @@ import Collection from '@discordjs/collection'; -import { CallEvents, CallHistoryItem } from '../../../typings/call'; +import { CallEvents, CallHistoryItem, CallRejectReasons } from '../../../typings/call'; import CallsDB, { CallsRepo } from './calls.db'; import { v4 as uuidv4 } from 'uuid'; import PlayerService from '../players/player.service'; @@ -131,7 +131,7 @@ class CallsService { emitNet(CallEvents.FETCH_CALLS, src, calls); } - async handleRejectCall(src: number, transmitterNumber: string): Promise { + async handleRejectCall(src: number, transmitterNumber: string, reason: CallRejectReasons): Promise { const currentCall = this.callMap.get(transmitterNumber); const endCallTimeUnix = Math.floor(new Date().getTime() / 1000); @@ -149,6 +149,13 @@ class CallsService { // player who is calling and recieved the rejection. emitNet(CallEvents.WAS_REJECTED, currentCall.transmitterSource); + if (reason && CallRejectReasons[reason]) { + emitNet(CallEvents.SEND_ALERT, src, { + message: `DIALER_${reason}`, + type: 'error', + }); + } + await this.callsDB.updateCall(currentCall, false, endCallTimeUnix); this.callMap.delete(transmitterNumber); @@ -166,19 +173,12 @@ class CallsService { return; } - // player who is being called - emitNet(CallEvents.WAS_ENDED, currentCall.receiverSource); + if (currentCall.is_accepted) { + emitNet(CallEvents.WAS_ENDED, currentCall.receiverSource); + } // player who is calling emitNet(CallEvents.WAS_ENDED, currentCall.transmitterSource); - // ends animations if call is active - // TODO: This animation flow needs to be taken a look at. We should be able to - // to just attach this to the WAS_ENDED event without another net event. - if (currentCall.accepted) { - emitNet(CallEvents.SEND_HANGUP_ANIM, currentCall.receiverSource); - emitNet(CallEvents.SEND_HANGUP_ANIM, currentCall.transmitterSource); - } - await this.callsDB.updateCall(currentCall, true, endCallTimeUnix); // Clear from memory diff --git a/resources/yarn.lock b/resources/yarn.lock index ea1362e9d..4748b1afe 100644 --- a/resources/yarn.lock +++ b/resources/yarn.lock @@ -929,15 +929,15 @@ resolved "https://registry.yarnpkg.com/@citizenfx/client/-/client-1.0.3281-1.tgz#e26d85cb3686f8e0c85fdb86dca159e027358418" integrity sha512-hZ82+Z1CfFFWYqTohDc9K2Cn00NAGQ4Hv34JH9kQpMsA2talHVUXjZOZ78/Dtk0DcGVA2beSYfA8fMm6ZrKmHg== -"@citizenfx/client@^2.0.4035-1": - version "2.0.4035-1" - resolved "https://registry.yarnpkg.com/@citizenfx/client/-/client-2.0.4035-1.tgz#3b8527b1a1411e93835ee9b77672f9d492c5ad00" - integrity sha512-+cTYEBCabs9QdHBMT1pn6YzlggATktvds/yWiJv+OGtqY/zKpQE1anBDMfHrGuw92WxveR1VNndInTKY88O0bg== - -"@citizenfx/server@^2.0.4035-1": - version "2.0.4035-1" - resolved "https://registry.yarnpkg.com/@citizenfx/server/-/server-2.0.4035-1.tgz#517a91a7d189330d194b442a0f9a8ec858b4541b" - integrity sha512-D8ZtB7jhcGoLAbW0BZjxHGtuLrFiRAfaN2HjSbaBxJaYk5c4Fobg0RkS2tCr+PlidpARPaW1eq/maqYtNB2c0g== +"@citizenfx/client@^2.0.4170-1": + version "2.0.4170-1" + resolved "https://registry.yarnpkg.com/@citizenfx/client/-/client-2.0.4170-1.tgz#66d03899f387b160e28a057c5810fed1476998b6" + integrity sha512-5Wj6k/zUmNKCpOsZCbUbTaWYqnSBQJPztvx7ln51DAktTRO2l29mRMfcBZNVQ2L3tJt144w9iTrlmytojuzrdQ== + +"@citizenfx/server@^2.0.4167-1": + version "2.0.4167-1" + resolved "https://registry.yarnpkg.com/@citizenfx/server/-/server-2.0.4167-1.tgz#c5450eff0ab38e98ffed592831d004c9ad7d535e" + integrity sha512-hVusU0cemvfFBXcDS+BRdMWa59hdCTQK51iva1dAC0KPGnhkqs5XC3BAVhCwjDbv2Fx3Vy1Oasz3KU6/RG6EkQ== "@cnakazawa/watch@^1.0.3": version "1.0.4" diff --git a/typings/call.ts b/typings/call.ts index 6e1d90d4b..fd2f16938 100644 --- a/typings/call.ts +++ b/typings/call.ts @@ -18,6 +18,11 @@ export interface CallHistoryItem { is_accepted: number; } +export enum CallRejectReasons { + DECLINED, + BUSY_LINE +} + export enum CallEvents { INITIALIZE_CALL = 'npwd:beginCall', START_CALL = 'npwd:startCall',