Pay N Spray

Started by habi, Apr 10, 2020, 05:28 AM

Previous topic - Next topic

habi

hi guys, i was working on pay n spray for 2 days. write, rewrite code..
Here we go screenshot

Server side
0. garage.nut  ?topic=7563.0 :P
[spoiler][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae]/**
 * @class Garage
 */
class Garage {
  /** Available garages. */
  static list = {

    /** Pay n' Spray (Little Haiti). */
     haiwshpnsdoor = {
      ID = 1573,
      Pos = Vector(-874.696, -116.695, 11.987),
      OpenPos = Vector(-874.696, -116.695, 15.987),
      Speed = 2000,
      SoundID = 176
    },

    /** Pay n' Spray (Viceport). */
     dk_paynspraydoor = {
      ID = 828,
      Pos = Vector(-910.001, -1264.709, 12.492),
      OpenPos = Vector(-910.001, -1264.709, 16.492),
      Speed = 2000,
      SoundID = 176
    },

    /** Pay n' Spray (Washington Beach). */
     wshpnsdoor = {
      ID = 3013,
      Pos = Vector(-7.804, -1257.642, 10.818),
      OpenPos = Vector(-7.804, -1257.642, 15.818),
      Speed = 2000,
      SoundID = 176
    },

    /** Pay n' Spray (Vice Point). */
     nbecpnsdoor = {
      ID = 4145,
      Pos = Vector(325.083, 431.136, 11.587),
      OpenPos = Vector(325.083, 431.136, 16.587),
      Speed = 2000,
      SoundID = 176,
      Rotation = Vector(0, 0, 2.810)
    }
  }

  /**
   * Hide a garage.
   *
   * @param {string} name Garage name (see Garage.list).
   * @return {boolean}
   */
  static function hide(name) {
    if(name in Garage.list) {
      local garage = Garage.list[name];
      ::HideMapObject(garage.ID, garage.Pos.x, garage.Pos.y, garage.Pos.z);
      return true;
    }

    return false;
  }

  /**
   * Create a garage.
   *
   * @param {string} name Garage name (see Garage.list).
   * @return {boolean}
   */
  static function create(name) {
    if(name in Garage.list) {
      Garage.hide(name);

      local garage = Garage.list[name];
      local instance = ::CreateObject(garage.ID, 0, garage.Pos, 255);

      if("Rotation" in garage) {
        instance.RotateToEuler(garage.Rotation, 0);
      }

      return Garage(instance, garage.OpenPos, garage.Pos, garage.Speed, garage.SoundID);
    }

    return null;
  }

  /** Check if garage is open or closed. */
  _closed = true

  /** Garage object reference. */
  instance = null

  /** Garage position when it's open. */
  openPosition = Vector(0, 0, 0)

  /** Garage position when it's closed. */
  closedPosition = Vector(0, 0, 0)

  /** Opening and closing speed. */
  speed = 1000

  /** Sound played when a garage opens or closes. */
  soundID = 176

  /**
   * @constructor
   *
   * @param {instance} instance Garage object reference.
   * @param {Vector} openPosition Garage position when it's open.
   * @param {Vector} closedPosition Garage position when it's closed.
   * @param {number} speed Opening and closing speed.
   * @param {number} soundID Sound played when a garage opens or closes.
   */
  constructor(instance, openPosition, closedPosition, speed, soundID) {
    this._closed = true;
    this.instance = instance;
    this.openPosition = openPosition;
    this.closedPosition = closedPosition;
    this.speed = speed;
    this.soundID = soundID;
  }

  /**
   * Open this garage.
   *
   * @return {boolean}
   */
  function open() {
    if(this.instance != null && this._closed == true) {
      ::PlaySound(0, this.soundID, this.closedPosition);
      this.instance.MoveTo(this.openPosition, this.speed);
      this._closed = false;
      return true;
    }

    return false;
  }

  /**
   * Close this garage.
   *
   * @return {boolean}
   */
  function close() {
    if(this.instance != null && this._closed == false) {
      ::PlaySound(0, this.soundID, this.closedPosition);
      this.instance.MoveTo(this.closedPosition, this.speed);
      this._closed = true;
      return true;
    }

    return false;
  }

  /**
   * Switch between opening and closing the garage.
   *
   * @param {boolean}
   */
  function toggle() {
    if(this._closed == true) {
      this.open();
    }
    else {
      this.close();
    }

    return true;
  }

  /**
   * Check if garage is open or closed.
   *
   * @return {boolean}
   */
  function isClosed() {
    return this._closed;
  }
}
[/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/spoiler]
1.main.nut
[spoiler][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae]enum StreamType
{
pspray_enter=0x12,
pspray_exit=0x13,
vehicle_enter=0x14,
vehicle_exit=0x15
}
function onScriptLoad()
{
dofile("scripts/garage.nut");
dofile("scripts/pay_nspray.nut");onPayNSprayScriptLoad();
}
function onPlayerEnterVehicle( player, vehicle, door )
{
if( door == 0 )SendDataToClient( player, StreamType.vehicle_enter );
}
function onPlayerExitVehicle( player, vehicle )
{
SendDataToClient( player, StreamType.vehicle_exit );
}
function onClientScriptData( player )
{
    // receiving client data
    local stream = Stream.ReadByte();
    switch ( stream )
    {
case StreamType.pspray_enter:
{
onPayNSprayEntered( player );
}
break;
case StreamType.pspray_exit:
{
onPayNSprayExited( player );
}
break;
default:
break;

    }
}
[/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/spoiler]
2.pay_nspray.nut
[spoiler][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae]dofile("scripts/pspray_class.nut");
function onPayNSprayExited( player )
{
if( player.CameraLocked )
player.RestoreCamera();
}
function onPayNSprayEntered( player )
{
local p = FindPayNSpray( player );
if( p )
{
if( p.busy )return;
local pos = p.enter;
local lookat = Vector( pos.x, pos.y, p.floor );
player.SetCameraPos( p.campos, lookat);
local vehicle = player.Vehicle;
if( !vehicle )return;
if(vehicle.Driver.Name!=player.Name)return;
if( !IsPolice( vehicle.Model ) ) // ;)
{
if( player.Cash >= 100 )
{
p.Garage.close();
p.busy = true;
player.IsFrozen=true;
NewTimer("ReSpray",3600,1, player.Name, p, vehicle.ID);
}else Announce("~b~No more freebies!. 0 to respray!", player,6);
}else Announce("~b~Whoa! I don't touch nothing THAT hot!", player,6);
}

}
function ReSpray( player_name, p, vehicle_id)
{
local player = FindPlayer( player_name );
local vehicle = FindVehicle( vehicle_id );
if( player )
{
if( player.Vehicle && player.Vehicle.ID==vehicle.ID)
{
if( vehicle.Health < 1000 )
{
vehicle.Fix();
vehicle.Colour1 = rand()%94;
vehicle.Colour2 = rand()%94;
Announce("~b~New engine and paint job. The cops won't recognize you!", player,6);
if( player.Cash >=100 )player.Cash-= 100;
}else
{
vehicle.Colour1 = rand()%94;
vehicle.Colour2 = rand()%94;
Announce("~b~Respray is complementary", player,6);
}

}
player.IsFrozen=false;
}
p.Garage.open();
p.busy = false;
}
function FindPayNSpray( player )
{
local v = player.Pos;
foreach( p in psprays )
{
if( InPoly( v.x, v.y, p.p1.x, p.p1.y, p.p2.x, p.p2.y, p.p3.x, p.p3.y, p.p4.x,
p.p4.y ) )
{
if(  v.z < p.top && v.z > p.floor-2.0 )
{
return p;
}
}
}
return null;
}
function onPayNSprayScriptLoad()
{
psprays<-[];
psprays.push( PnSpray.create("haiti","haiwshpnsdoor") );
psprays.push( PnSpray.create("viceport","dk_paynspraydoor") );
psprays.push( PnSpray.create("washington","wshpnsdoor") );
psprays.push( PnSpray.create("vicepoint","nbecpnsdoor") );
}
function IsPolice( model )
{
return O( model, 137,145,155,156,157,162,163,227,236 );
}
function O(t,...)
{
foreach(val in vargv)
{
if(t==val)return 1;
}
return 0;
}
function move(pos, distance, angle)
{
local newx = pos.x - sin(angle)*distance;
local newy = pos.y + cos(angle)*distance;
return Vector(newx,newy,pos.z);//assuming z more or less same.
}
function move2(pos,pangle,dis,angle)
{
return move(pos,dis,pangle+angle)
}
[/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/spoiler]
3. pspray_class.nut
[spoiler][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae]class
PI<-3.1415926;
PnSpray
{
static list = {
haiti = {
p1= Vector(-876.235,-119.503,0),
p2= Vector(-873.388,-113.763,0),
p3= Vector(-882.621,-109.039,0),
p4= Vector(-885.522,-114.767,0),
floor = 11.1034,
top= 20.3105,
FacingAngle = 1.08
},
viceport = {
p1=Vector(-913.569,-1263.01,0),
p2=Vector(-906.448,-1266.15,0),
p3=Vector(-899.628,-1250.29,0),
p4=Vector(-906.842,-1247.23,0),
floor = 11.75,
top = 20.8162,
FacingAngle= -0.457
},
washington = {
p1=Vector(-7.42133,-1255.2,0),
p2=Vector(-7.32615,-1260.22,0),
p3=Vector(2.34665,-1260.19,0),
p4=Vector(2.34665,-1255.1,0),
floor= 10.4633,
top= 20.6906,
FacingAngle= -1.522
},
vicepoint = {
p1=Vector(324.209,428.838,0)
p2=Vector(325.718,433.625,0)
p3=Vector(316.283,436.631,0)
p4=Vector(314.645,431.77,0)
floor=11.2,
top=21.4596,
FacingAngle=1.286
}
}
enter = null;
p1=null;
p2=null;
p3=null;
p4=null;
floor=null;
top=null;
FacingAngle=null;
Garage=null;
campos=null;
busy = null;
        markerID=null;
//----------------------------------------------------------------------------------------
constructor( p1, p2, p3, p4, floor, top, fangle, garage_name, markerID )
{
this.enter = ( p1 + p2 ) * 0.5;
this.campos = move2( this.enter, fangle, 15, PI ) + Vector( 0, 0, floor +3.9);
this.p1=p1;
this.p2=p2;
this.p3=p3;
this.p4=p4;
this.floor=floor;
this.top=top;
this.FacingAngle=fangle;
this.Garage=::Garage.create(garage_name);
if(this.Garage)this.Garage.open();
this.busy = false;
                this.markerID= markerID;
}
//------------------------------------------------------------------------------------------
static function create(name, garage_name) {
if(name in PnSpray.list) {
local pspray = PnSpray.list[name];
                        local markerID=::CreateMarker(1, pspray.p1, 1, RGB(0, 0, 0), 27 );
return PnSpray( pspray.p1, pspray.p2, pspray.p3, pspray.p4, pspray.floor,
pspray.top,pspray.FacingAngle,garage_name, markerID );

}
return null;
}
}
[/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/spoiler]
client side
1. main.nut
[spoiler][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae]enum StreamType
{
pspray_enter=0x12,
pspray_exit=0x13,
vehicle_enter=0x14,
vehicle_exit=0x15
}
Timer <- {
 Timers = {}

 function Create(environment, listener, interval, repeat, ...)
 {
  // Prepare the arguments pack
  vargv.insert(0, environment);

  // Store timer information into a table
  local TimerInfo = {
   Environment = environment,
   Listener = listener,
   Interval = interval,
   Repeat = repeat,
   Args = vargv,
   LastCall = Script.GetTicks(),
   CallCount = 0
  };

  local hash = split(TimerInfo.tostring(), ":")[1].slice(3, -1).tointeger(16);

  // Store the timer information
  Timers.rawset(hash, TimerInfo);

  // Return the hash that identifies this timer
  return hash;
 }

 function Destroy(hash)
 {
  // See if the specified timer exists
  if (Timers.rawin(hash))
  {
   // Remove the timer information
   Timers.rawdelete(hash);
  }
 }

 function Exists(hash)
 {
  // See if the specified timer exists
  return Timers.rawin(hash);
 }

 function Fetch(hash)
 {
  // Return the timer information
  return Timers.rawget(hash);
 }

 function Clear()
 {
  // Clear existing timers
  Timers.clear();
 }

 function Process()
 {
  local CurrTime = Script.GetTicks();
  foreach (hash, tm in Timers)
  {
   if (tm != null)
   {
    if (CurrTime - tm.LastCall >= tm.Interval)
    {
     tm.CallCount++;
     tm.LastCall = CurrTime;

     tm.Listener.pacall(tm.Args);

     if (tm.Repeat != 0 && tm.CallCount >= tm.Repeat)
      Timers.rawdelete(hash);
    }
   }
  }
 }
};
dofile("pspray.nut");
onPSprayScriptLoad();
function Script::ScriptProcess()
{
Timer.Process();
}
function Server::ServerData( stream )
{
   
    local type = stream.ReadByte();
    switch( type )
    {
               case StreamType.vehicle_enter:
{
::plr_veh = true;
check();
}break;
case StreamType.vehicle_exit:
{
::plr_veh = null;
}break;
        default:
        break;
    }
}
function SendDataToServer( ... )
{
    if( vargv[0] )
    {
        local   byte = vargv[0],
                len = vargv.len();
               
        if( 1 > len ) Console.Print( "ToClent <" + byte + "> No params specified." );
        else
        {
            local pftStream = Stream();
            pftStream.WriteByte( byte );

            for( local i = 1; i < len; i++ )
            {
                switch( typeof( vargv[i] ) )
                {
                    case "integer": pftStream.WriteInt( vargv[i] ); break;
                    case "string": pftStream.WriteString( vargv[i] ); break;
                    case "float": pftStream.WriteFloat( vargv[i] ); break;
                }
            }
           
            Server.SendData( pftStream );
        }
    }
    else Console.Print( "ToClient: Not even the byte was specified..." );
}
[/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/spoiler]
2. pspray.nut
[spoiler][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae][noae]class PnSpray
{
static list = {
haiti = {
p1= Vector(-876.235,-119.503,0),
p2= Vector(-873.388,-113.763,0),
p3= Vector(-882.621,-109.039,0),
p4= Vector(-885.522,-114.767,0),
floor = 11.1034,
top= 20.3105,
FacingAngle = 1.08
},
viceport = {
p1=Vector(-913.569,-1263.01,0),
p2=Vector(-906.448,-1266.15,0),
p3=Vector(-899.628,-1250.29,0),
p4=Vector(-906.842,-1247.23,0),
floor = 11.75,
top = 20.8162,
FacingAngle= -0.457
},
washington = {
p1=Vector(-7.42133,-1255.2,0),
p2=Vector(-7.32615,-1260.22,0),
p3=Vector(2.34665,-1260.19,0),
p4=Vector(2.34665,-1255.1,0),
floor= 10.4633,
top= 20.6906,
FacingAngle= -1.522
},
vicepoint = {
p1=Vector(324.209,428.838,0),
p2=Vector(325.718,433.625,0),
p3=Vector(316.283,436.631,0),
p4=Vector(314.645,431.77,0),
floor=11.2,
top=21.4596,
FacingAngle=1.286
}
}
enter = null;
p1=null;
p2=null;
p3=null;
p4=null;
floor=null;
top=null;
FacingAngle=null;
//----------------------------------------------------------------------------------------
constructor( p1, p2, p3, p4, floor, top, fangle )
{
this.enter= Vector( (p1.X + p2.X)* 0.5, (p1.Y + p2.Y)* 0.5, 0 );
this.p1=p1;
this.p2=p2;
this.p3=p3;
this.p4=p4;
this.floor=floor;
this.top=top;
this.FacingAngle=fangle;
}
//------------------------------------------------------------------------------------------
static function create(name) {
if(name in PnSpray.list) {
local pspray = PnSpray.list[name];
return PnSpray( pspray.p1, pspray.p2, pspray.p3, pspray.p4, pspray.floor,
pspray.top,pspray.FacingAngle);
}
return null;
}
}
function onPSprayScriptLoad()
{
MAX_SPEED<-60;//ie. about 210 km/h
plr_veh<-null;
p_state<-false;
psprays<-[];
psprays.push( PnSpray.create("haiti") );
psprays.push( PnSpray.create("viceport") );
psprays.push( PnSpray.create("washington") );
psprays.push( PnSpray.create("vicepoint") );
Console.Print("Pay and Spray Script Loaded");
}
function IsInsideAnyPSpray()
{
local player = World.FindLocalPlayer();
local s =  Vector(player.Position);
local t, dis;
foreach(p in psprays)
{
if( InPoly( s, p.p1, p.p2, p.p3, p.p4 ) )
{
if( s.Z < p.top && s.Z > p.floor - 2.0 )
{
t = p.enter;
dis = sqrt( pow( t.X - s.X , 2 ) + pow( t.Y - s.Y , 2 ) + pow( p.floor - s.Z, 2 ) )
if( dis > 3.4 )return true;
}
}
}
return false;
}
function InPoly( v, ... )
{
foreach( i, p in vargv )
{
if( i + 2 < vargv.len() )
{
if( InTriangle( v, vargv[0], vargv[i+1], vargv[i+2] ) )return true;
}else
return false;
}
}
function InTriangle( v, p, q, r )
{
return InStrip( v, p, q, r ) && InStrip( v, q, r, p ) && InStrip( v, r, p, q );
}
function InStrip( v, p, q, r )
{
if( q.X == p.X )
{
return r.X > p.X ? v.X >= p.X && v.X <= r.X : v.X >= r.X && v.X <= p.X;
}else
{
local m = ( 1.0*q.Y - p.Y ) / ( q.X - p.X );
local c1 = p.Y - m * p.X ;
local c2 = r.Y - m * r.X;
local c = v.Y - m * v.X;
return c2 > c1 ? c <= c2 && c >= c1 : c <= c1 && c >= c2 ;
}
}
function check()
{
if( ::plr_veh== null )
{
return;
}
if( ::p_state==false )
{
if( IsInsideAnyPSpray() )
{
//inform server that player entered a pay and spray.
SendDataToServer( StreamType.pspray_enter );
::p_state = true;
}
}else
{
if( !IsInsideAnyPSpray() )
{
//inform server that player exited
SendDataToServer( StreamType.pspray_exit );
::p_state = false;
}
}
local player = World.FindLocalPlayer();
local s =  Vector(player.Position);
local nearest_pspray = null;
local dis;local min=null;local t ;

foreach(p in psprays)
{
t = p.enter;
dis = sqrt( pow( t.X - s.X , 2 ) + pow( t.Y - s.Y , 2 ) + pow( p.floor - s.Z, 2 ) )
if( min==null)
{
min=dis;
nearest_pspray=p;
}else if( dis < min )
{
min = dis;
nearest_pspray=p;
}
}
local distance = min;
local min_time;
if( distance < 180 )
{
if( distance < 8 )min_time = 500;
else if( distance < 25 )min_time = 1000;
else if( distance < 35 )min_time = 2000;
else if( distance < 90 )min_time = 2500;
else if( distance < 120 )min_time = 3500;
else min_time = 5000;
}else min_time = distance / MAX_SPEED * 1000;
Timer.Create(this, check, min_time, 1 );
}
[/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/noae][/spoiler]
video:- https://youtu.be/4XDM21ZHfNw
How it works?
simple. the client checks every second, whether the player is inside any Pay N Spray. it informs the server when player enters Pay N Spray with a car. Server then sets the camera, lock the garage, fix the vehicle and reduce the cash.
When u are away from Pay N Spray, server restore camera.
Also if u are far away from Pay N Spray, client timer also 'relax' to 10 sec, 15 sec, etc. It depends on the distance.
In client main.nut, I had made use of ysc3839's client timers.

Sebastian

You should adapt this to work with 8ball garage too, and make a topic like "Singleplayer Features" where you bring everything together: ammu-nations, pns and 8ball.