Roblox
Belajar Luau 15: Struktur Kode dengan Modul, OOP, dan Tipe
Menjaga kode game Roblox tetap rapi saat membesar: ModuleScript, OOP dengan metatable, pewarisan class, struktur folder proyek, dan anotasi tipe dengan --!strict.

Game kecil bisa berjalan dengan beberapa script panjang. Tapi begitu game membesar, misalnya 30 jenis senjata, 10 jenis musuh, dan pet, kode yang tidak tertata cepat menjadi sulit diubah tanpa merusak bagian lain. Pelajaran ini membahas tiga alat untuk menjaga kode tetap rapi.
Pelajaran ini bisa dijalankan di terminal (Linux maupun Windows), dari folder belajar-luau:
luau 15-struktur-kode/1-modul.luau
luau 15-struktur-kode/2-oop-class.luau
luau 15-struktur-kode/3-pewarisan.luau
luau 15-struktur-kode/4-tipe-data.luau
luau-analyze 15-struktur-kode/4-tipe-data.luau
Di Windows: unduh
luau-windows.zipdari https://github.com/luau-lang/luau/releases, ekstrak, lalu letakkanluau.exedanluau-analyze.exedi folder yang ada di PATH, atau jalankan langsung dari folder tersebut.
| File | Isi |
|---|---|
1-modul.luau + modul/Ekonomi.luau |
Memecah kode ke modul, function publik dan privat |
2-oop-class.luau + modul/Musuh.luau |
Class, objek, constructor, method, titik vs titik dua |
3-pewarisan.luau + modul/Bos.luau |
Inheritance: Bos mewarisi Musuh lalu menimpa sebagian perilakunya |
4-tipe-data.luau |
Anotasi tipe dan --!strict, supaya error ketahuan sebelum game dijalankan |
Konsep 1: Modul
Kamu sudah memakai modul sejak Pelajaran 9 (KonfigToko). Sekarang modul dipakai untuk function, bukan hanya data. Aturannya:
- Satu modul berisi satu tugas, misalnya Ekonomi, Pertarungan, atau Format
- Yang di-return adalah "pintu depan" (publik). Yang tidak di-return bersifat privat.
- Modul hanya dijalankan sekali. Semua yang me-
require-nya mendapat table yang sama.
Konsep 2: OOP dengan metatable
local Musuh = {}
Musuh.__index = Musuh -- (1) objek mencari function di sini
function Musuh.new(nama, darah) -- (2) constructor, pakai titik
local self = setmetatable({}, Musuh) -- (3) hubungkan objek ke class
self.nama = nama
self.darah = darah
return self
end
function Musuh:terimaDamage(n) -- (4) method, pakai titik dua -> dapat "self"
self.darah -= n
end
local slime = Musuh.new("Slime", 30)
slime:terimaDamage(5)
Polanya selalu sama, jadi cukup dihafal saja. Penjelasan lengkap tiap baris ada di modul/Musuh.luau.
Konsep 3: Contoh OOP di Roblox
OOP paling berguna untuk objek yang punya Part dan perilaku sendiri. Contohnya class Pintu yang membungkus sebuah Part:
-- ReplicatedStorage/Kelas/Pintu.luau (ModuleScript)
local TweenService = game:GetService("TweenService")
local Pintu = {}
Pintu.__index = Pintu
function Pintu.new(part: BasePart)
local self = setmetatable({}, Pintu)
self.part = part
self.posisiTutup = part.CFrame
self.terbuka = false
return self
end
function Pintu:buka()
if self.terbuka then return end
self.terbuka = true
local tujuan = self.posisiTutup + Vector3.new(0, self.part.Size.Y, 0)
TweenService:Create(self.part, TweenInfo.new(0.5), {CFrame = tujuan}):Play()
end
function Pintu:tutup()
if not self.terbuka then return end
self.terbuka = false
TweenService:Create(self.part, TweenInfo.new(0.5), {CFrame = self.posisiTutup}):Play()
end
return Pintu
-- Script di server: semua pintu di folder "Pintu" langsung jalan
local Pintu = require(game.ReplicatedStorage.Kelas.Pintu)
for _, part in workspace.Pintu:GetChildren() do
local pintu = Pintu.new(part)
part.Touched:Connect(function()
pintu:buka()
task.wait(3)
pintu:tutup()
end)
end
Bandingkan dengan Pelajaran 12: logika pintunya sama, tapi sekarang bisa dipakai untuk puluhan pintu sekaligus, dan menambah fitur seperti :kunci() cukup dilakukan di satu tempat.
Konsep 4: Struktur folder untuk game yang membesar
Saat proyek game/ (Pelajaran 14) bertambah besar, susunannya bisa berkembang menjadi seperti ini:
src/
├── server/
│ ├── Main.server.luau ← satu script "pintu masuk" yang memanggil layanan
│ └── Layanan/ ← modul KHUSUS server (aman dari exploiter)
│ ├── DataPemain.luau
│ ├── Toko.luau
│ └── Pembelian.luau
├── client/
│ ├── Main.client.luau
│ └── UI/
│ ├── UIKoin.luau
│ └── UIToko.luau
└── shared/ ← dibaca server DAN client
├── Konfig/
│ ├── KonfigToko.luau
│ └── KonfigProduk.luau
└── Kelas/
└── Pintu.luau
Prinsipnya:
- Modul server diletakkan di ServerScriptService atau ServerStorage, karena client tidak bisa membacanya
- Modul shared di ReplicatedStorage: hanya untuk hal yang boleh dilihat semua orang
- Sedikit Script/LocalScript, banyak modul
Kamu tidak perlu langsung mengubah proyek game/ menjadi seperti ini. Lakukan pelan-pelan saat script mulai terasa terlalu panjang.
Konsep 5: Tipe
Lihat 4-tipe-data.luau. Dengan --!strict dan ekstensi Luau Language Server di VS Code (Pelajaran 14), kesalahan seperti salah ketik nama properti atau salah tipe argumen langsung ditandai merah saat kamu menulis kode, bukan saat pemain sudah memainkan game-mu.
Kode
Jalankan file-file utama dari folder belajar-luau dengan luau. Modul di folder modul/ dipinjam lewat require.
1-modul.luau
-- require = memuat modul. Di terminal, modul dicari berdasarkan PATH
-- file (relatif terhadap file ini). Di Roblox, require memakai Instance:
-- require(ReplicatedStorage.KonfigToko)
local Ekonomi = require("./modul/Ekonomi")
print("Harga pedang:", Ekonomi.formatAngka(1250000), "koin")
print("Diskon 25%:", Ekonomi.hargaSetelahDiskon(200, 25))
print("Dengan pajak", Ekonomi.PAJAK_PERSEN .. "%:", Ekonomi.hargaDenganPajak(200))
print("Jual kembali barang seharga 230:", Ekonomi.hargaJualKembali(230))
-- Function privat TIDAK bisa diakses dari luar modul:
print("bulatkanKe5 dari luar:", Ekonomi.bulatkanKe5) -- nil
-- Modul hanya dijalankan SEKALI. require berikutnya mengembalikan
-- table yang SAMA (bukan salinan), jadi bisa dipakai untuk berbagi data.
local EkonomiLagi = require("./modul/Ekonomi")
print("Table yang sama?", Ekonomi == EkonomiLagi) -- true
-- ============================================================
-- KAPAN MEMBUAT MODUL?
-- ============================================================
-- * Kode yang sama dipakai di 2 tempat atau lebih
-- (contoh: format angka di UI koin DAN UI toko)
-- * Data konfigurasi (KonfigToko, KonfigProduk)
-- * Satu script sudah terlalu panjang (lebih dari ~300 baris).
-- Pecah berdasarkan TUGASNYA: data, toko, pertarungan, dll.
modul/Ekonomi.luau
- Dipakai oleh: 1-modul.luau
-- Modul = table berisi data dan function, lalu di-return di akhir
local Ekonomi = {}
-- "Konstanta" (nilai yang tidak diubah) biasanya ditulis HURUF_BESAR
Ekonomi.PAJAK_PERSEN = 10
-- Mengubah 1250000 menjadi "1.250.000"
function Ekonomi.formatAngka(angka)
local teks = tostring(math.floor(angka))
local hasil = ""
local hitung = 0
-- Baca dari belakang, sisipkan titik setiap 3 digit
for i = #teks, 1, -1 do
hasil = string.sub(teks, i, i) .. hasil
hitung += 1
if hitung % 3 == 0 and i > 1 then
hasil = "." .. hasil
end
end
return hasil
end
function Ekonomi.hargaSetelahDiskon(harga, diskonPersen)
return math.floor(harga * (100 - diskonPersen) / 100)
end
function Ekonomi.hargaDenganPajak(harga)
return math.ceil(harga * (100 + Ekonomi.PAJAK_PERSEN) / 100)
end
-- Function "privat": hanya dipakai di dalam modul ini, TIDAK dimasukkan
-- ke table Ekonomi, jadi tidak bisa dipanggil dari luar.
local function bulatkanKe5(angka)
return math.floor(angka / 5 + 0.5) * 5
end
function Ekonomi.hargaJualKembali(hargaBeli)
-- Barang bekas dijual 40% dari harga beli, dibulatkan ke kelipatan 5
return bulatkanKe5(hargaBeli * 0.4)
end
return Ekonomi
2-oop-class.luau
local Musuh = require("./modul/Musuh")
-- Membuat beberapa OBJEK dari class yang sama
local slime = Musuh.new("Slime", 30, 5)
local goblin = Musuh.new("Goblin", 50, 8)
-- Setiap objek punya data sendiri
print(slime:info()) -- Slime (30/30)
print(goblin:info()) -- Goblin (50/50)
-- Memanggil method dengan TITIK DUA
slime:terimaDamage(12)
print(slime:info()) -- Slime (18/30)
print(goblin:info()) -- Goblin (50/50) <- tidak ikut berubah
-- Objek bisa saling berinteraksi
local pemain = Musuh.new("Budi", 40, 15) -- pakai class yang sama untuk contoh
print("--- Pertarungan ---")
while pemain:masihHidup() and goblin:masihHidup() do
pemain:serang(goblin)
if goblin:masihHidup() then
goblin:serang(pemain)
end
print(` -> {pemain:info()} vs {goblin:info()}`)
end
print(if pemain:masihHidup() then "Budi menang!" else "Budi kalah...")
-- ============================================================
-- TITIK (.) vs TITIK DUA (:)
-- ============================================================
-- Kedua baris ini SAMA PERSIS:
print(slime:info())
print(slime.info(slime))
-- Titik dua hanyalah singkatan yang otomatis mengirim objeknya
-- sebagai parameter pertama (self).
--
-- Kesalahan umum: slime.info() -> error, karena self = nil
-- ============================================================
-- KENAPA OOP?
-- ============================================================
-- Tanpa OOP, data musuh tersebar di banyak table dan function
-- terpisah. Dengan OOP, data DAN perilakunya dibungkus jadi satu:
-- "sebuah Musuh tahu cara menyerang dan menerima damage".
-- Ini sangat membantu untuk game besar dengan banyak jenis objek:
-- senjata, pet, NPC, pintu, kendaraan, dll.
modul/Musuh.luau
- Dipakai oleh: 2-oop-class.luau, modul/Bos.luau
-- Table "Musuh" berperan sebagai CLASS: cetakan untuk membuat banyak
-- objek musuh yang punya data sendiri-sendiri tapi function yang sama.
local Musuh = {}
-- __index: kalau sebuah objek tidak punya kunci tertentu (misalnya
-- function "serang"), Luau akan mencarinya di table Musuh.
Musuh.__index = Musuh
-- CONSTRUCTOR: membuat objek baru.
-- Pakai TITIK (.) karena belum ada objeknya.
function Musuh.new(nama, darah, serangan)
-- setmetatable menghubungkan table kosong ini ke class Musuh
local self = setmetatable({}, Musuh)
-- Data milik objek ini saja
self.nama = nama
self.darah = darah
self.darahMaks = darah
self.serangan = serangan
return self
end
-- METHOD: function milik objek.
-- Pakai TITIK DUA (:) -> otomatis mendapat parameter tersembunyi "self",
-- yaitu objek yang memanggilnya.
function Musuh:masihHidup()
return self.darah > 0
end
-- Return true kalau musuh mati karena damage ini
function Musuh:terimaDamage(jumlah)
self.darah = math.max(self.darah - jumlah, 0)
return self.darah == 0
end
function Musuh:serang(target)
print(` {self.nama} menyerang {target.nama} sebesar {self.serangan}`)
return target:terimaDamage(self.serangan)
end
function Musuh:info()
return `{self.nama} ({self.darah}/{self.darahMaks})`
end
return Musuh
3-pewarisan.luau
local Musuh = require("./modul/Musuh")
local Bos = require("./modul/Bos")
local pemain = Musuh.new("Budi", 120, 14)
local rajaOrc = Bos.new("Raja Orc", 100, 9)
print(rajaOrc:info()) -- 👑 Raja Orc (100/100) <- info() versi Bos
print("Hidup?", rajaOrc:masihHidup()) -- masihHidup() DIWARISI dari Musuh
print("--- Pertarungan bos ---")
local ronde = 0
while pemain:masihHidup() and rajaOrc:masihHidup() do
ronde += 1
print(`Ronde {ronde}`)
pemain:serang(rajaOrc) -- memanggil terimaDamage() versi BOS
if rajaOrc:masihHidup() then
rajaOrc:serang(pemain) -- serang() diwarisi dari Musuh
end
print(` -> {pemain:info()} vs {rajaOrc:info()}`)
end
print(if pemain:masihHidup() then "🏆 Budi mengalahkan bos!" else "💀 Budi kalah...")
-- ============================================================
-- Memeriksa class sebuah objek
-- ============================================================
print("rajaOrc adalah Bos?", getmetatable(rajaOrc) == Bos) -- true
print("pemain adalah Bos?", getmetatable(pemain) == Bos) -- false
-- ============================================================
-- LATIHAN
-- ============================================================
-- 1. Buat class "Penyembuh" turunan Musuh yang punya method
-- :sembuhkan(target) untuk memulihkan darah target 20 poin
-- (tidak boleh melebihi darahMaks).
-- 2. Tambahkan method :reset() di Musuh yang mengembalikan darah ke
-- darahMaks. Apakah Bos otomatis bisa memakainya? Kenapa?
-- 3. Bos yang sudah marah dan di-reset seharusnya tidak marah lagi.
-- Override :reset() di Bos untuk menangani ini.
modul/Bos.luau
- Dipakai oleh: 3-pewarisan.luau
local Musuh = require("./Musuh")
-- Kalau Bos tidak punya suatu function, cari di Musuh
local Bos = setmetatable({}, {__index = Musuh})
Bos.__index = Bos
function Bos.new(nama, darah, serangan)
-- Buat objek dengan constructor Musuh dulu (supaya data dasarnya ada)...
local self = Musuh.new(nama, darah, serangan)
-- ...lalu "pindahkan" ke class Bos
setmetatable(self, Bos)
-- Data tambahan khusus Bos
self.marah = false
return self
end
-- OVERRIDE: menimpa method milik Musuh dengan versi Bos
function Bos:terimaDamage(jumlah)
-- Panggil versi ASLI milik Musuh (pakai titik dan kirim self manual)
local mati = Musuh.terimaDamage(self, jumlah)
if not mati and not self.marah and self.darah <= self.darahMaks / 2 then
self.marah = true
self.serangan *= 2
print(` 🔥 {self.nama} MARAH! Serangan jadi {self.serangan}`)
end
return mati
end
function Bos:info()
return "👑 " .. Musuh.info(self)
end
return Bos
4-tipe-data.luau
--!strict
-- Baris "--!strict" di paling atas menyalakan pemeriksaan tipe ketat.
-- Luau akan memberi tahu kesalahan SEBELUM game dijalankan.
-- Di VS Code, ekstensi Luau Language Server menandainya dengan garis merah.
-- 1. TIPE pada variabel dan parameter: tulis ": tipe"
local namaGame: string = "Obby Belajar"
local maksPemain: number = 20
-- 2. TIPE pada function: parameter dan nilai return
local function hitungHadiah(stage: number, isVIP: boolean): number
local hadiah = stage * 10
if isVIP then
hadiah *= 2
end
return hadiah
end
print(namaGame, maksPemain, hitungHadiah(5, true))
-- 3. Membuat TIPE SENDIRI untuk table
type Item = {
id: string,
nama: string,
harga: number,
stok: number?, -- tanda ? = boleh nil (opsional)
}
local pedang: Item = {id = "Pedang", nama = "Pedang Besi", harga = 250}
local ramuan: Item = {id = "Ramuan", nama = "Ramuan", harga = 50, stok = 10}
-- 4. Array bertipe: {Item}
local function totalHarga(daftar: {Item}): number
local total = 0
for _, item in daftar do -- di Luau, ipairs boleh dihilangkan
total += item.harga
end
return total
end
print("Total:", totalHarga({pedang, ramuan}))
-- 5. Tipe gabungan (union): salah satu dari beberapa nilai
type Kelangkaan = "Biasa" | "Langka" | "Legendaris"
local function warnaKelangkaan(k: Kelangkaan): string
if k == "Legendaris" then
return "emas"
elseif k == "Langka" then
return "biru"
end
return "abu-abu"
end
print("Legendaris ->", warnaKelangkaan("Legendaris"))
-- ============================================================
-- COBA SENDIRI: hapus tanda "--" di depan salah satu baris di bawah,
-- lalu jalankan luau-analyze. Error-nya terdeteksi tanpa menjalankan game.
-- ============================================================
-- hitungHadiah("lima", true) -- string bukan number
-- local rusak: Item = {id = "X", nama = "X"} -- harga belum diisi
-- warnaKelangkaan("Mitos") -- bukan salah satu pilihan
-- ============================================================
-- PERLU PAKAI TIPE?
-- ============================================================
-- Tidak wajib. Tapi untuk game besar, tipe sangat membantu:
-- * Salah ketik nama properti langsung ketahuan
-- * Autocomplete di VS Code jauh lebih pintar
-- * Kode lebih mudah dipahami orang lain (dan dirimu 3 bulan lagi)
-- Saran: mulai pakai --!strict di modul-modul baru.
Latihan
- Kerjakan 3 latihan di akhir
3-pewarisan.luau(class Penyembuh,:reset(), dan override reset di Bos). - Class Item: buat
modul/Item.luaudengan constructorItem.new(nama, harga, kelangkaan)dan method:hargaJual()yang memakaiEkonomi.hargaJualKembali. Artinya, modul memakai modul lain. - Tas: buat class
Tasdengan method:tambah(item),:buang(nama),:totalNilai(), dan batas maksimal 10 item. Uji di file5-latihan-tas.luau. - Tipe: tambahkan
--!strictdan anotasi tipe kemodul/Ekonomi.luau, lalu jalankanluau-analyzesampai bersih.
Kunci jawaban
Coba kerjakan sendiri dulu sebelum membaca bagian ini.
Semua jawaban bisa dijalankan dari terminal. File utamanya jawaban.luau memakai modul-modul jawaban di folder modul/:
luau 15-struktur-kode/jawaban.luau
Latihan 1 (pewarisan): class Penyembuh
modul/Penyembuh.luau, turunan dari Musuh dengan method baru :sembuhkan():
local Musuh = require("./Musuh")
local Penyembuh = setmetatable({}, {__index = Musuh})
Penyembuh.__index = Penyembuh
local JUMLAH_PULIH = 20
function Penyembuh.new(nama, darah, serangan)
local self = Musuh.new(nama, darah, serangan)
setmetatable(self, Penyembuh)
return self
end
-- Method BARU yang tidak dimiliki Musuh biasa
function Penyembuh:sembuhkan(target)
if not self:masihHidup() then
return -- penyembuh yang sudah kalah tidak bisa menyembuhkan
end
local sebelum = target.darah
target.darah = math.min(target.darah + JUMLAH_PULIH, target.darahMaks)
print(` ✚ {self.nama} menyembuhkan {target.nama}: {sebelum} -> {target.darah}`)
end
return Penyembuh
Latihan 2 dan 3 (pewarisan): reset() di Musuh dan override di Bos
Di modul/Musuh.luau, tambahkan method:
-- Kembalikan darah ke penuh
function Musuh:reset()
self.darah = self.darahMaks
end
Apakah Bos otomatis bisa memakai reset() milik Musuh? Ya. Saat raja:reset() dipanggil, Luau mencari reset di objek, tidak ketemu, lalu mencari di Bos, dan karena Bos punya __index = Musuh, akhirnya ketemu di Musuh. Itulah pewarisan.
Masalahnya, reset() versi Musuh hanya memulihkan darah. Bos yang sudah marah tetap punya serangan 2× dan marah = true, sehingga saat darahnya turun lagi ia tidak bisa "marah" lagi, dan serangannya sudah 2× sejak awal. Karena itu Bos butuh reset sendiri. Di modul/Bos.luau, simpan serangan awal di constructor lalu override reset():
function Bos.new(nama, darah, serangan)
local self = Musuh.new(nama, darah, serangan)
setmetatable(self, Bos)
self.marah = false
self.seranganAwal = serangan -- diingat untuk reset
return self
end
-- Override reset: selain darah penuh, mode marah juga hilang
function Bos:reset()
Musuh.reset(self) -- jalankan reset versi Musuh dulu
self.marah = false
self.serangan = self.seranganAwal
end
Latihan 2 (panduan): class Item yang memakai modul Ekonomi
local Ekonomi = require("./Ekonomi")
local Item = {}
Item.__index = Item
function Item.new(nama, harga, kelangkaan)
local self = setmetatable({}, Item)
self.nama = nama
self.harga = harga
self.kelangkaan = kelangkaan or "Biasa"
return self
end
-- Memakai function dari modul Ekonomi, jadi aturan harga jual
-- cukup ditulis di SATU tempat (Ekonomi), bukan diulang di sini.
function Item:hargaJual()
return Ekonomi.hargaJualKembali(self.harga)
end
function Item:info()
return `{self.nama} [{self.kelangkaan}] {Ekonomi.formatAngka(self.harga)} koin`
end
return Item
Latihan 3 (panduan): class Tas
local Tas = {}
Tas.__index = Tas
local KAPASITAS_BAWAAN = 10
function Tas.new(kapasitas)
local self = setmetatable({}, Tas)
self.isi = {} -- array berisi objek Item
self.kapasitas = kapasitas or KAPASITAS_BAWAAN
return self
end
-- Return true kalau berhasil, false + alasan kalau gagal
function Tas:tambah(item)
if #self.isi >= self.kapasitas then
return false, "Tas penuh"
end
table.insert(self.isi, item)
return true
end
-- Membuang item PERTAMA dengan nama tersebut
function Tas:buang(nama)
for i, item in ipairs(self.isi) do
if item.nama == nama then
table.remove(self.isi, i)
return item
end
end
return nil
end
function Tas:totalNilai()
local total = 0
for _, item in ipairs(self.isi) do
total += item.harga
end
return total
end
function Tas:jumlah()
return #self.isi
end
return Tas
Latihan 4 (panduan): Ekonomi dengan --!strict
Cek dengan luau-analyze 15-struktur-kode/modul/Ekonomi.luau:
--!strict
local Ekonomi = {}
Ekonomi.PAJAK_PERSEN = 10
function Ekonomi.formatAngka(angka: number): string
local teks = tostring(math.floor(angka))
local hasil = ""
local hitung = 0
for i = #teks, 1, -1 do
hasil = string.sub(teks, i, i) .. hasil
hitung += 1
if hitung % 3 == 0 and i > 1 then
hasil = "." .. hasil
end
end
return hasil
end
function Ekonomi.hargaSetelahDiskon(harga: number, diskonPersen: number): number
return math.floor(harga * (100 - diskonPersen) / 100)
end
function Ekonomi.hargaDenganPajak(harga: number): number
return math.ceil(harga * (100 + Ekonomi.PAJAK_PERSEN) / 100)
end
local function bulatkanKe5(angka: number): number
return math.floor(angka / 5 + 0.5) * 5
end
function Ekonomi.hargaJualKembali(hargaBeli: number): number
return bulatkanKe5(hargaBeli * 0.4)
end
return Ekonomi
Menguji semuanya: jawaban.luau
local Musuh = require("./modul/Musuh")
local Bos = require("./modul/Bos")
local Penyembuh = require("./modul/Penyembuh")
local Item = require("./modul/Item")
local Tas = require("./modul/Tas")
-- ------------------------------------------------------------
-- Latihan 1: Penyembuh
-- ------------------------------------------------------------
print("=== Latihan 1: Penyembuh ===")
local goblin = Musuh.new("Goblin", 50, 8)
local dukun = Penyembuh.new("Dukun Goblin", 30, 3)
goblin:terimaDamage(35)
print(goblin:info()) -- Goblin (15/50)
dukun:sembuhkan(goblin) -- 15 -> 35
dukun:sembuhkan(goblin) -- 35 -> 50 (bukan 55, dibatasi darahMaks)
print(dukun:info()) -- method info() DIWARISI dari Musuh
-- ------------------------------------------------------------
-- Latihan 2: reset() di Musuh
-- ------------------------------------------------------------
print("=== Latihan 2: reset ===")
local raja = Bos.new("Raja Orc", 100, 9)
raja:terimaDamage(60) -- darah 40 -> MARAH, serangan 18
print(raja:info(), "serangan:", raja.serangan)
-- Pertanyaan: apakah Bos otomatis bisa memakai reset() milik Musuh?
-- Jawaban: YA. Saat raja:reset() dipanggil, Luau mencari "reset" di
-- objek -> tidak ada -> cari di Bos -> (sebelum latihan 3) tidak ada ->
-- cari di Musuh (karena Bos punya __index = Musuh) -> ketemu.
-- Itulah pewarisan.
-- ------------------------------------------------------------
-- Latihan 3: override reset() di Bos
-- ------------------------------------------------------------
-- Masalahnya: reset() versi Musuh hanya memulihkan darah. Bos yang
-- sudah marah tetap punya serangan 2x dan marah = true, jadi saat
-- darahnya turun lagi, ia tidak akan "marah" lagi (karena sudah true),
-- dan serangannya sudah 2x sejak awal. Karena itu Bos butuh reset sendiri.
print("=== Latihan 3: reset Bos ===")
raja:reset() -- memanggil Bos:reset() (override)
print(raja:info(), "serangan:", raja.serangan, "marah:", raja.marah)
-- 👑 Raja Orc (100/100) serangan: 9 marah: false
raja:terimaDamage(55) -- bisa MARAH lagi
print("serangan setelah marah lagi:", raja.serangan) -- 18
-- ------------------------------------------------------------
-- PANDUAN latihan 2: Item memakai Ekonomi
-- ------------------------------------------------------------
print("=== Item ===")
local pedang = Item.new("Pedang Naga", 12500, "Legendaris")
print(pedang:info()) -- Pedang Naga [Legendaris] 12.500 koin
print("Harga jual:", pedang:hargaJual()) -- 40% dari 12500 = 5000
-- ------------------------------------------------------------
-- PANDUAN latihan 3: Tas dengan batas 10 item
-- ------------------------------------------------------------
print("=== Tas ===")
local tas = Tas.new() -- kapasitas bawaan 10
for i = 1, 12 do
local berhasil, alasan = tas:tambah(Item.new(`Batu {i}`, 10))
if not berhasil then
print(`Item ke-{i} gagal masuk: {alasan}`) -- item 11 dan 12
end
end
print("Isi tas:", tas:jumlah(), "| total nilai:", tas:totalNilai()) -- 10 | 100
local dibuang = tas:buang("Batu 3")
print("Dibuang:", dibuang and dibuang.nama, "| isi sekarang:", tas:jumlah())
print("Buang yang tidak ada:", tas:buang("Berlian")) -- nil
print("Bisa tambah lagi?", tas:tambah(pedang)) -- true
print("Total nilai:", tas:totalNilai()) -- 90 + 12500
Discussion (0)
We store your optional name, your comment, and technical hashes (not your raw IP address) to prevent spam. A functional cookie lets you edit or delete your own comments from this browser. To request deletion, use the contact page.
No comments yet. Start the discussion.