lt-script 1.0.1 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +548 -15
- package/dist/cli/ltc.js +44 -34
- package/dist/cli/utils.d.ts +40 -0
- package/dist/cli/utils.js +40 -0
- package/dist/compiler/codegen/LuaEmitter.js +20 -3
- package/dist/compiler/lexer/Lexer.js +7 -2
- package/dist/compiler/lexer/Token.d.ts +2 -1
- package/dist/compiler/lexer/Token.js +6 -2
- package/dist/compiler/parser/AST.d.ts +18 -0
- package/dist/compiler/parser/AST.js +3 -0
- package/dist/compiler/parser/Parser.d.ts +6 -0
- package/dist/compiler/parser/Parser.js +216 -31
- package/dist/compiler/semantics/SemanticAnalyzer.d.ts +28 -0
- package/dist/compiler/semantics/SemanticAnalyzer.js +682 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +9 -1
- package/package.json +12 -3
package/README.md
CHANGED
|
@@ -1,33 +1,566 @@
|
|
|
1
|
-
# LT Language
|
|
1
|
+
# LT Language
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://img.shields.io/npm/v/lt-script?color=blue&label=npm" alt="npm version">
|
|
5
|
+
<img src="https://img.shields.io/github/license/laot7490/lt-script" alt="license">
|
|
6
|
+
<img src="https://img.shields.io/badge/FiveM-Ready-green" alt="FiveM Ready">
|
|
7
|
+
</p>
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
**LT** is a modern, statically-typed superset of Lua designed specifically for **FiveM development**. It combines the elegance of TypeScript with the performance of Lua, adding FiveM-specific syntactic sugar to make your scripts robust, readable, and faster to write.
|
|
10
|
+
|
|
11
|
+
> **Goodbye boilerplate, hello productivity.**
|
|
12
|
+
> Write clean, type-safe code that compiles to optimized 100% vanilla Lua.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## ✨ Why LT?
|
|
17
|
+
|
|
18
|
+
* **🛡️ Type Safety**: Catch type mismatches and `nil` errors *before* you run the game.
|
|
19
|
+
* **⚡ Modern Syntax**: Use Arrow Functions, Destructuring, Spread syntax, and more.
|
|
20
|
+
* **🎮 FiveM-First**: Built-in keywords like `netevent`, `addcmd`, `thread`, and `wait`.
|
|
21
|
+
* **🔌 Zero Overhead**: Compiles to pure, readable Lua with no runtime dependencies.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 📦 Installation
|
|
26
|
+
|
|
27
|
+
Prerequisites: [Node.js](https://nodejs.org/) (Version 16+)
|
|
6
28
|
|
|
7
29
|
```bash
|
|
30
|
+
# Global installation (Recommended)
|
|
8
31
|
npm install -g lt-script
|
|
32
|
+
|
|
33
|
+
# OR run directly via npx
|
|
34
|
+
npx lt-script build .
|
|
9
35
|
```
|
|
10
36
|
|
|
11
|
-
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 🚀 Quick Start
|
|
12
40
|
|
|
41
|
+
Create a `client.lt` file:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// Define a type for player data
|
|
45
|
+
interface PlayerData {
|
|
46
|
+
id: number,
|
|
47
|
+
name: string,
|
|
48
|
+
health: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Variables with types
|
|
52
|
+
let active: boolean = false
|
|
53
|
+
const MAX_HEALTH = 100
|
|
54
|
+
|
|
55
|
+
// FiveM Command
|
|
56
|
+
addcmd "heal" (source, args)
|
|
57
|
+
let amount = tonumber(args[1]) ?? MAX_HEALTH
|
|
58
|
+
SetEntityHealth(PlayerPedId(), amount)
|
|
59
|
+
print("Healed for ${amount}")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
// Thread with loop
|
|
63
|
+
thread
|
|
64
|
+
print("System active")
|
|
65
|
+
loop (true)
|
|
66
|
+
wait 1000
|
|
67
|
+
if LocalPlayer.state?.isDead then
|
|
68
|
+
print("Player is dead!")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Compile it:**
|
|
13
75
|
```bash
|
|
14
|
-
ltc
|
|
76
|
+
ltc build .
|
|
15
77
|
```
|
|
16
78
|
|
|
17
|
-
|
|
79
|
+
---
|
|
18
80
|
|
|
19
|
-
|
|
20
|
-
|
|
81
|
+
## 📘 Language Reference
|
|
82
|
+
|
|
83
|
+
### 1. Variables & Types
|
|
84
|
+
|
|
85
|
+
LT supports explicit typing with compile-time validation.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// Mutable variables
|
|
89
|
+
let score: number = 0
|
|
90
|
+
var playerName: string = "John" // Type inferred if omitted
|
|
91
|
+
|
|
92
|
+
// Constants (Compiles to Lua 5.4 <const>)
|
|
93
|
+
const MAX_PLAYERS = 32
|
|
94
|
+
|
|
95
|
+
// Available primitive types
|
|
96
|
+
let str: string = "hello"
|
|
97
|
+
let num: number = 42
|
|
98
|
+
let flag: boolean = true
|
|
99
|
+
let data: table = { key: "value" }
|
|
100
|
+
let pos: vector3 = <100.0, 200.0, 30.0>
|
|
101
|
+
let callback: function = () => print("hi")
|
|
102
|
+
|
|
103
|
+
// Arrays
|
|
104
|
+
let inventory: string[] = ["Apple", "Water"]
|
|
105
|
+
let numbers: number[] = [1, 2, 3]
|
|
21
106
|
```
|
|
22
107
|
|
|
23
|
-
###
|
|
108
|
+
### 2. Interfaces & Type Aliases
|
|
24
109
|
|
|
25
|
-
|
|
26
|
-
|
|
110
|
+
Define complex data structures for compile-time validation.
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Interface - Object shape definition
|
|
114
|
+
interface VehicleConfig {
|
|
115
|
+
model: string,
|
|
116
|
+
price: number,
|
|
117
|
+
color: string
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Type Alias - Alternative syntax
|
|
121
|
+
type PlayerPos = { x: number, y: number, z: number }
|
|
122
|
+
|
|
123
|
+
// Usage with validation
|
|
124
|
+
const myCar: VehicleConfig = {
|
|
125
|
+
model: "sultan",
|
|
126
|
+
price: 50000,
|
|
127
|
+
color: "red"
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Arrays of interfaces
|
|
131
|
+
interface StoreConfig {
|
|
132
|
+
id: number,
|
|
133
|
+
position: vector3,
|
|
134
|
+
isOpen: boolean
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const Stores: StoreConfig[] = [
|
|
138
|
+
{ id: 1, position: <100.0, 200.0, 30.0>, isOpen: true },
|
|
139
|
+
{ id: 2, position: <150.0, 250.0, 35.0>, isOpen: false }
|
|
140
|
+
]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
> **Note:** Type validation ensures field types match. Assigning `number` to a `vector3` field will cause a compile error.
|
|
144
|
+
|
|
145
|
+
### 3. Functions
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// Standard function with typed parameters
|
|
149
|
+
func Add(a: number, b: number)
|
|
150
|
+
return a + b
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
// Default parameters
|
|
154
|
+
func Greet(name: string, prefix = "Hello")
|
|
155
|
+
print("${prefix}, ${name}!")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
// Local function (Lua local function)
|
|
159
|
+
local function PrivateHelper()
|
|
160
|
+
return "internal"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
// Arrow functions
|
|
164
|
+
let double = (x: number) => x * 2
|
|
165
|
+
let greet = (name) => print("Hello ${name}")
|
|
166
|
+
|
|
167
|
+
// Multi-line arrow
|
|
168
|
+
let process = (data) => {
|
|
169
|
+
let result = data * 2
|
|
170
|
+
return result
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 4. Control Flow
|
|
175
|
+
|
|
176
|
+
#### If / Else
|
|
177
|
+
```typescript
|
|
178
|
+
// Note: 'then' keyword is required after condition
|
|
179
|
+
if health < 20 then
|
|
180
|
+
print("Low health!")
|
|
181
|
+
elseif health < 50 then
|
|
182
|
+
print("Medium health")
|
|
183
|
+
else
|
|
184
|
+
print("Full health")
|
|
185
|
+
end
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### Switch / Case
|
|
189
|
+
```typescript
|
|
190
|
+
let job = "police"
|
|
191
|
+
|
|
192
|
+
switch job
|
|
193
|
+
case "police", "ambulance"
|
|
194
|
+
print("Government worker")
|
|
195
|
+
case "mechanic"
|
|
196
|
+
print("Service worker")
|
|
197
|
+
default
|
|
198
|
+
print("Civilian")
|
|
199
|
+
end
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### Guard Clauses
|
|
203
|
+
Clean up nested if-statements with early returns.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
func RevivePlayer(player)
|
|
207
|
+
// Returns immediately if player is nil
|
|
208
|
+
guard player return
|
|
209
|
+
|
|
210
|
+
// Returns with custom logic if condition fails
|
|
211
|
+
guard player.isDead else
|
|
212
|
+
print("Player is already alive")
|
|
213
|
+
return
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
// Main logic
|
|
217
|
+
player.revive()
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
// Guard with return value
|
|
221
|
+
func GetPlayer(id)
|
|
222
|
+
let player = GetPlayerById(id)
|
|
223
|
+
guard player return nil
|
|
224
|
+
return player
|
|
225
|
+
end
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### For Loops
|
|
229
|
+
```typescript
|
|
230
|
+
// Numeric for (start, end)
|
|
231
|
+
for i = 1, 10 do
|
|
232
|
+
print(i)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
// Numeric for with step (start, end, step)
|
|
236
|
+
for i = 0, 100, 5 do
|
|
237
|
+
print(i)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
// Range for with step
|
|
241
|
+
for i in 1..10 by 2 do
|
|
242
|
+
print(i)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
// For-in (pairs)
|
|
246
|
+
for key, value in pairs(myTable) do
|
|
247
|
+
print("${key}: ${value}")
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
// For-in (ipairs)
|
|
251
|
+
for index, item in ipairs(myArray) do
|
|
252
|
+
print("${index}: ${item}")
|
|
253
|
+
end
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### While Loop
|
|
257
|
+
```typescript
|
|
258
|
+
let count = 0
|
|
259
|
+
while count < 10 do
|
|
260
|
+
print(count)
|
|
261
|
+
count += 1
|
|
262
|
+
end
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### 5. Modern Operators
|
|
266
|
+
|
|
267
|
+
#### Compound Assignment
|
|
268
|
+
```typescript
|
|
269
|
+
let x = 10
|
|
270
|
+
x += 5
|
|
271
|
+
x -= 3
|
|
272
|
+
x *= 2
|
|
273
|
+
x /= 4
|
|
274
|
+
x %= 3
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
#### Null Coalescing (`??`)
|
|
278
|
+
```typescript
|
|
279
|
+
let name = playerName ?? "Unknown"
|
|
280
|
+
let health = GetHealth() ?? 100
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
#### Optional Chaining (`?.`)
|
|
284
|
+
```typescript
|
|
285
|
+
let state = LocalPlayer.state?.isDead
|
|
286
|
+
let nested = obj?.deep?.value
|
|
287
|
+
let arrItem = arr?[0]
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
#### Ternary Operator
|
|
291
|
+
```typescript
|
|
292
|
+
let status = isDead ? "Dead" : "Alive"
|
|
293
|
+
let label = count > 0 ? "${count} items" : "Empty"
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### 6. Destructuring & Spread
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
// Object Destructuring
|
|
300
|
+
let { x, y, z } = GetEntityCoords(ped)
|
|
301
|
+
let { name, health } = playerData
|
|
302
|
+
|
|
303
|
+
// Array Destructuring
|
|
304
|
+
let [first, second] = GetValues()
|
|
305
|
+
|
|
306
|
+
// Object Spread (tables only)
|
|
307
|
+
let base = { a: 1, b: 2 }
|
|
308
|
+
let extended = { ...base, c: 3 }
|
|
27
309
|
```
|
|
28
310
|
|
|
29
|
-
|
|
311
|
+
### 7. String Interpolation
|
|
312
|
+
|
|
313
|
+
Use `${expression}` inside strings to embed values:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
let name = "John"
|
|
317
|
+
let age = 25
|
|
318
|
+
|
|
319
|
+
print("Hello, ${name}!")
|
|
320
|
+
print("You are ${age} years old")
|
|
321
|
+
print("Next year you'll be ${age + 1}")
|
|
322
|
+
|
|
323
|
+
// Complex expressions
|
|
324
|
+
let player = { health: 100 }
|
|
325
|
+
print("Health: ${player.health}")
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 8. Vector Literals
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// Vector3 (FiveM native)
|
|
332
|
+
let pos: vector3 = <100.0, 200.0, 30.0>
|
|
333
|
+
|
|
334
|
+
// Vector2
|
|
335
|
+
let screenPos: vector2 = <0.5, 0.5>
|
|
336
|
+
|
|
337
|
+
// Vector4 (includes heading)
|
|
338
|
+
let spawn: vector4 = <100.0, 200.0, 30.0, 90.0>
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 🎮 FiveM Features
|
|
344
|
+
|
|
345
|
+
### Thread & Loop
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
// Create a thread (Citizen.CreateThread)
|
|
349
|
+
thread
|
|
350
|
+
print("Thread started")
|
|
351
|
+
|
|
352
|
+
loop (true)
|
|
353
|
+
wait 0 // Run every frame
|
|
354
|
+
|
|
355
|
+
// Game logic here
|
|
356
|
+
let ped = PlayerPedId()
|
|
357
|
+
let health = GetEntityHealth(ped)
|
|
358
|
+
|
|
359
|
+
if health < 50 then
|
|
360
|
+
print("Low health warning!")
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Wait
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
// Wait in milliseconds
|
|
370
|
+
wait 1000 // Wait 1 second
|
|
371
|
+
wait 0 // Yield one frame
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Network Events
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
// Register + Handle network event (Client <-> Server)
|
|
378
|
+
netevent "bank:deposit" (amount)
|
|
379
|
+
print("Depositing ${amount}")
|
|
380
|
+
UpdateBankBalance(amount)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
// Local event handler
|
|
384
|
+
event "onClientResourceStart" (resName)
|
|
385
|
+
guard resName == GetCurrentResourceName() return
|
|
386
|
+
print("Resource started!")
|
|
387
|
+
end
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Emit Events
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
// Emit to server
|
|
394
|
+
emit "server:event:name" (arg1, arg2)
|
|
395
|
+
|
|
396
|
+
// Explicit emit variants
|
|
397
|
+
emitServer "myevent" (data)
|
|
398
|
+
emitClient "myevent" (targetPlayer, data)
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Commands
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
// Register a command (RegisterCommand wrapper)
|
|
405
|
+
addcmd "teleport" (source, args)
|
|
406
|
+
let x = tonumber(args[1]) ?? 0
|
|
407
|
+
let y = tonumber(args[2]) ?? 0
|
|
408
|
+
let z = tonumber(args[3]) ?? 0
|
|
409
|
+
|
|
410
|
+
SetEntityCoords(PlayerPedId(), x, y, z)
|
|
411
|
+
print("Teleported to ${x}, ${y}, ${z}")
|
|
412
|
+
end
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Exports
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
// Export a function
|
|
419
|
+
export "GetPlayerMoney" ()
|
|
420
|
+
return playerMoney
|
|
421
|
+
end
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Timeout & Interval
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
// Run once after delay
|
|
428
|
+
timeout 5000
|
|
429
|
+
print("5 seconds passed!")
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
// Run repeatedly
|
|
433
|
+
interval 1000
|
|
434
|
+
print("Every second")
|
|
435
|
+
end
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Try / Catch
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
try
|
|
442
|
+
RiskyOperation()
|
|
443
|
+
catch err
|
|
444
|
+
print("Error: ${err}")
|
|
445
|
+
end
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## ⚡ Complete Example: Store Robbery System
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
// 1. Type Definition
|
|
454
|
+
interface StoreConfig {
|
|
455
|
+
id: number,
|
|
456
|
+
position: vector3,
|
|
457
|
+
reward: number,
|
|
458
|
+
isOpen: boolean
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// 2. Configuration
|
|
462
|
+
const Stores: StoreConfig[] = [
|
|
463
|
+
{ id: 1, position: <120.5, -500.2, 30.0>, reward: 5000, isOpen: true },
|
|
464
|
+
{ id: 2, position: <250.0, 300.0, 25.0>, reward: 10000, isOpen: true }
|
|
465
|
+
]
|
|
466
|
+
|
|
467
|
+
let isRobbing = false
|
|
468
|
+
|
|
469
|
+
// 3. Main Game Loop
|
|
470
|
+
thread
|
|
471
|
+
loop (true)
|
|
472
|
+
wait 0
|
|
473
|
+
|
|
474
|
+
if isRobbing then
|
|
475
|
+
break
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
let ped = PlayerPedId()
|
|
479
|
+
let pos = GetEntityCoords(ped)
|
|
480
|
+
|
|
481
|
+
for _, store in ipairs(Stores) do
|
|
482
|
+
if not store.isOpen then
|
|
483
|
+
break
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
let distance = #(pos - store.position)
|
|
487
|
+
if distance < 2.0 then
|
|
488
|
+
ShowHelpText("Press ~INPUT_CONTEXT~ to rob")
|
|
489
|
+
|
|
490
|
+
if IsControlJustPressed(0, 38) then
|
|
491
|
+
StartRobbery(store)
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
// 4. Robbery Function
|
|
499
|
+
func StartRobbery(store: StoreConfig)
|
|
500
|
+
isRobbing = true
|
|
501
|
+
|
|
502
|
+
// Notify server
|
|
503
|
+
emit "robbery:start" (store.id)
|
|
504
|
+
|
|
505
|
+
// Play animation
|
|
506
|
+
TaskPlayAnim(PlayerPedId(), "anim_dict", "anim_name", 8.0, -8.0, -1, 0, 0, false, false, false)
|
|
507
|
+
|
|
508
|
+
wait 5000
|
|
509
|
+
|
|
510
|
+
ClearPedTasks(PlayerPedId())
|
|
511
|
+
isRobbing = false
|
|
512
|
+
|
|
513
|
+
print("Robbery complete! Reward: ${store.reward}")
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
// 5. Server Sync Handler
|
|
517
|
+
netevent "robbery:sync" (storeId, newState)
|
|
518
|
+
let store = Stores.find(s => s.id == storeId)
|
|
519
|
+
guard store return
|
|
520
|
+
|
|
521
|
+
store.isOpen = newState
|
|
522
|
+
let stateStr = newState ? "open" : "closed"
|
|
523
|
+
print("Store ${storeId} is now ${stateStr}")
|
|
524
|
+
end
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## 🛠️ CLI Reference
|
|
530
|
+
|
|
531
|
+
| Command | Usage | Description |
|
|
532
|
+
|:--------|:------|:------------|
|
|
533
|
+
| **Build** | `ltc build ./` | Compiles all `.lt` files in the folder to `.lua` |
|
|
534
|
+
| **Watch** | `ltc watch ./` | Watches for file changes and recompiles instantly |
|
|
535
|
+
| **Version** | `ltc -v` | Displays the current compiler version |
|
|
536
|
+
|
|
537
|
+
### Project Structure
|
|
538
|
+
|
|
539
|
+
LT automatically detects `src/` folders:
|
|
540
|
+
|
|
541
|
+
```
|
|
542
|
+
my-resource/
|
|
543
|
+
├── src/
|
|
544
|
+
│ ├── client.lt
|
|
545
|
+
│ └── server.lt
|
|
546
|
+
├── client.lua ← Generated
|
|
547
|
+
└── server.lua ← Generated
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## 🔧 VS Code Extension
|
|
553
|
+
|
|
554
|
+
Get the official **[LT Language Extension](https://marketplace.visualstudio.com/items?itemName=laot.lt-language)** for the best experience.
|
|
555
|
+
|
|
556
|
+
**Features:**
|
|
557
|
+
* 🎨 Full Syntax Highlighting
|
|
558
|
+
* 🔍 Intelligent Autocompletion
|
|
559
|
+
* 📦 **FiveM Native Intellisense**
|
|
560
|
+
* ✨ Real-time Error Checking
|
|
561
|
+
|
|
562
|
+
---
|
|
30
563
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
564
|
+
<p align="center">
|
|
565
|
+
Made with ❤️ for the FiveM Community by <b>LaotScripts</b>
|
|
566
|
+
</p>
|