Skip to content

RevScript System Complete Guide

Mateuzkl edited this page Apr 24, 2026 · 1 revision

📜 RevScript System — Complete Guide

Modern scripting system for TFS 1.8 Downgrade 8.60 — No XML registration required


🚀 What is RevScript?

RevScript is a modern alternative way to register scripts in TFS without needing XML files. Instead of editing actions.xml, movements.xml, talkactions.xml, etc., you simply place your Lua scripts inside data/scripts/ (or any subfolder), and they are automatically registered.

Key Advantages

Traditional System RevScript System
Requires XML registration Auto-registration
Separate files for each event type Multiple event types in one file
Manual ID/word mapping Programmatic registration
Harder to maintain Cleaner and more organized

📁 File Structure

Script Locations

data/
├── scripts/              ← Place your RevScripts here
│   ├── actions/
│   ├── creatureevents/
│   ├── globalevents/
│   ├── movements/
│   ├── spells/
│   ├── talkactions/
│   └── weapons/
└── monster/              ← Monster scripts go here
    └── scripts/

💡 Tip: You can organize scripts in any subfolder structure you want inside data/scripts/


🧩 Available Metatables

RevScript supports the following event types:

Metatable Usage
Action() Item usage (onUse)
CreatureEvent("name") Player/creature events (login, logout, death, etc.)
GlobalEvent("name") Server-wide events (startup, shutdown, timers)
MonsterType("name") Monster definitions and behaviors
MoveEvent() Tile stepping, item equip/deequip
Spell("name") Spell casting
TalkAction("words") Chat commands
Weapon(WEAPON_TYPE) Weapon attacks

📝 Basic Script Structure

Every RevScript follows this pattern:

-- 1. Header: Create the metatable
local scriptName = Metatable()

-- 2. Configuration (optional)
scriptName:method(value)

-- 3. Function: Define the behavior
function scriptName.onEvent(parameters)
    -- Your code here
    return true
end

-- 4. Registration: Register IDs, words, etc.
scriptName:id(1234)

-- 5. Footer: Register the script
scriptName:register()

🔧 Action() — Item Usage

Basic Example

local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    player:sendTextMessage(MESSAGE_STATUS_DEFAULT, "You used item: " .. item:getId())
    return true
end

action:id(2550) -- Scythe
action:register()

Available Methods

Method Description Example
id(ids) Register by item ID(s) action:id(2550) or action:id(2550, 2551, 2552)
aid(ids) Register by Action ID(s) action:aid(1000)
uid(ids) Register by Unique ID(s) action:uid(5000)
allowFarUse(bool) Allow use from distance action:allowFarUse(true)
blockWalls(bool) Block use through walls action:blockWalls(false)
checkFloor(bool) Require same floor level action:checkFloor(false)

Advanced Example: Quest Chest

local questChest = Action()

function questChest.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local storage = 10001
    
    if player:getStorageValue(storage) == 1 then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The chest is empty.")
        return true
    end
    
    player:addItem(2160, 10) -- 10 crystal coins
    player:setStorageValue(storage, 1)
    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have found 10 crystal coins!")
    return true
end

questChest:uid(5001)
questChest:register()

👤 CreatureEvent("name") — Player Events

Available Interfaces

Interface Triggered When
onLogin(player) Player logs in
onLogout(player) Player logs out
onThink(creature, interval) Creature thinks (periodic)
onPrepareDeath(creature, killer) Before creature dies
onDeath(creature, corpse, killer, ...) Creature dies
onKill(creature, target) Creature kills another
onAdvance(player, skill, oldLevel, newLevel) Player advances skill/level
onModalWindow(player, modalWindowId, buttonId, choiceId) Modal window interaction
onTextEdit(player, item, text) Player edits text (books, signs)
onHealthChange(creature, attacker, ...) Health changes
onManaChange(creature, attacker, ...) Mana changes
onExtendedOpCode(player, opcode, buffer) Extended opcode received

Example: Welcome Message on Login

local loginEvent = CreatureEvent("WelcomeMessage")

function loginEvent.onLogin(player)
    player:sendTextMessage(MESSAGE_STATUS_DEFAULT, "Welcome to the server, " .. player:getName() .. "!")
    player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    return true
end

loginEvent:register()

Example: Level Up Reward

local levelReward = CreatureEvent("LevelReward")

function levelReward.onAdvance(player, skill, oldLevel, newLevel)
    if skill ~= SKILL_LEVEL then
        return true
    end
    
    if newLevel >= 100 and oldLevel < 100 then
        player:addItem(2160, 50) -- 50 crystal coins
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Congratulations on reaching level 100! You received 50 crystal coins!")
    end
    
    return true
end

levelReward:register()

🌍 GlobalEvent("name") — Server Events

Available Interfaces

Interface Triggered When
onThink(interval) Periodic execution
onTime(interval) Specific time of day
onStartup() Server starts
onShutdown() Server shuts down
onRecord(current, old) New player record

Example: Server Startup Message

local startup = GlobalEvent("ServerStartup")

function startup.onStartup()
    print(">> Server has started successfully!")
    return true
end

startup:register()

Example: Daily Restart Warning

local restartWarning = GlobalEvent("DailyRestart")

function restartWarning.onTime(interval)
    Game.broadcastMessage("Server will restart in 5 minutes!", MESSAGE_STATUS_WARNING)
    return true
end

restartWarning:time("05:55") -- 5:55 AM daily
restartWarning:register()

Example: Periodic Event (Every 30 seconds)

local periodicEvent = GlobalEvent("PeriodicCheck")

function periodicEvent.onThink(interval)
    print("This runs every 30 seconds")
    return true
end

periodicEvent:interval(30000) -- 30000ms = 30 seconds
periodicEvent:register()

🚶 MoveEvent() — Tile & Equipment Events

Available Interfaces

Interface Triggered When
onEquip(player, item, slot, isCheck) Player equips item
onDeEquip(player, item, slot, isCheck) Player unequips item
onStepIn(creature, item, position, fromPosition) Creature steps on tile
onStepOut(creature, item, position, fromPosition) Creature leaves tile
onAddItem(moveitem, tileitem, position) Item added to tile
onRemoveItem(moveitem, tileitem, position) Item removed from tile

Available Methods

Method Description Example
id(ids) Register by item ID(s) moveevent:id(2550)
aid(ids) Register by Action ID(s) moveevent:aid(1000)
uid(ids) Register by Unique ID(s) moveevent:uid(5000)
position(positions) Register by position moveevent:position({x=100, y=100, z=7})
level(lvl) Minimum level required moveevent:level(50)
magiclevel(lvl) Minimum magic level moveevent:magiclevel(25)
premium(bool) Premium required moveevent:premium(true)
vocation(name, show, last) Vocation restriction moveevent:vocation("Sorcerer", true, false)

Example: Equipment Bonus

local equipBonus = MoveEvent()

function equipBonus.onEquip(player, item, slot, isCheck)
    if isCheck then
        return true
    end
    
    player:addHealth(500)
    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You feel healthier! (+500 HP)")
    return true
end

function equipBonus.onDeEquip(player, item, slot, isCheck)
    if isCheck then
        return true
    end
    
    player:addHealth(-500)
    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You feel weaker. (-500 HP)")
    return true
end

equipBonus:id(2472) -- Magic plate armor
equipBonus:register()

Example: Teleport Tile

local teleport = MoveEvent()

function teleport.onStepIn(creature, item, position, fromPosition)
    local player = creature:getPlayer()
    if not player then
        return true
    end
    
    local destination = Position(1000, 1000, 7)
    player:teleportTo(destination)
    destination:sendMagicEffect(CONST_ME_TELEPORT)
    return true
end

teleport:aid(1234)
teleport:register()

✨ Spell("name") — Magic Spells

Example: Instant Spell

local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_FIREDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_FIREAREA)
combat:setArea(createCombatArea(AREA_CIRCLE3X3))

function onGetFormulaValues(player, level, magicLevel)
    local min = (level / 5) + (magicLevel * 5) + 20
    local max = (level / 5) + (magicLevel * 8) + 40
    return -min, -max
end

combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")

local spell = Spell("instant")

function spell.onCastSpell(creature, variant)
    return combat:execute(creature, variant)
end

spell:name("Fire Bomb")
spell:words("exevo flam hur")
spell:group("attack")
spell:vocation("sorcerer", "master sorcerer")
spell:id(50)
spell:cooldown(4000) -- 4 seconds
spell:groupCooldown(2000) -- 2 seconds
spell:level(30)
spell:mana(150)
spell:isSelfTarget(true)
spell:isAggressive(true)
spell:needLearn(false)
spell:register()

Available Methods

Method Description
name(name) Spell name
words(words) Magic words
group(group) Spell group (attack, healing, support)
vocation(voc1, voc2, ...) Allowed vocations
id(id) Spell ID
cooldown(ms) Individual cooldown
groupCooldown(ms) Group cooldown
level(lvl) Minimum level
mana(mana) Mana cost
soul(soul) Soul cost
isPremium(bool) Premium required
isAggressive(bool) Aggressive spell
needLearn(bool) Needs to be learned
isSelfTarget(bool) Targets self

💬 TalkAction("words") — Chat Commands

Example: Simple Command

local hello = TalkAction("!hello")

function hello.onSay(player, words, param, type)
    player:say("Hello, " .. player:getName() .. "!", TALKTYPE_MONSTER_SAY)
    return true
end

hello:separator(" ")
hello:register()

Example: Teleport Command with Parameters

local teleportCommand = TalkAction("/tp")

function teleportCommand.onSay(player, words, param, type)
    if not player:getGroup():getAccess() then
        return false
    end
    
    local split = param:split(",")
    if #split < 3 then
        player:sendCancelMessage("Usage: /tp x,y,z")
        return false
    end
    
    local position = Position(tonumber(split[1]), tonumber(split[2]), tonumber(split[3]))
    player:teleportTo(position)
    position:sendMagicEffect(CONST_ME_TELEPORT)
    return true
end

teleportCommand:separator(" ")
teleportCommand:register()

⚔️ Weapon(WEAPON_TYPE) — Weapon Scripts

Weapon Types

  • WEAPON_SWORD
  • WEAPON_AXE
  • WEAPON_CLUB
  • WEAPON_SHIELD
  • WEAPON_DISTANCE
  • WEAPON_WAND
  • WEAPON_AMMO

Example: Custom Ammo

local explosiveArrow = Weapon(WEAPON_AMMO)

local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_EXPLOSIONAREA)
combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_BURSTARROW)
combat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
combat:setFormula(COMBAT_FORMULA_SKILL, 0, 0, 1.5, 0)
combat:setArea(createCombatArea(AREA_CIRCLE2X2))

function explosiveArrow.onUseWeapon(player, variant)
    return combat:execute(player, variant)
end

explosiveArrow:id(2547) -- Power bolt
explosiveArrow:register()

🐉 MonsterType("name") — Monster Definitions

Creating a Monster from Scratch

local mType = Game.createMonsterType("Demon Lord")
local monster = {}

monster.description = "a demon lord"
monster.experience = 10000
monster.outfit = {
    lookType = 35,
    lookHead = 0,
    lookBody = 0,
    lookLegs = 0,
    lookFeet = 0,
    lookAddons = 0,
    lookMount = 0
}

monster.health = 8200
monster.maxHealth = monster.health
monster.race = "fire"
monster.corpse = 5995
monster.speed = 280
monster.maxSummons = 2

monster.changeTarget = {
    interval = 4000,
    chance = 20
}

monster.flags = {
    summonable = false,
    attackable = true,
    hostile = true,
    convinceable = false,
    pushable = false,
    canPushItems = true,
    canPushCreatures = true,
    targetDistance = 1,
    staticAttackChance = 90
}

monster.summons = {
    {name = "Fire Elemental", chance = 15, interval = 2000, max = 2}
}

monster.voices = {
    interval = 5000,
    chance = 10,
    {text = "BOW TO THE POWER OF THE RUTHLESS SEVEN!", yell = true},
    {text = "SOULS FOR BELIAL!", yell = true}
}

monster.loot = {
    {id = "gold coin", chance = 100000, maxCount = 100},
    {id = "platinum coin", chance = 100000, maxCount = 50},
    {id = "crystal coin", chance = 50000, maxCount = 10},
    {id = "magic plate armor", chance = 1500},
    {id = "demon shield", chance = 2000}
}

monster.attacks = {
    {name = "melee", attack = 130, skill = 100, effect = CONST_ME_DRAWBLOOD, interval = 2000},
    {name = "combat", type = COMBAT_FIREDAMAGE, chance = 20, interval = 2000, minDamage = -200, maxDamage = -400, range = 7, radius = 3, shootEffect = CONST_ANI_FIRE, effect = CONST_ME_FIREAREA, target = true}
}

monster.defenses = {
    defense = 55,
    armor = 55,
    {name = "combat", type = COMBAT_HEALING, chance = 15, interval = 2000, minDamage = 200, maxDamage = 400, effect = CONST_ME_MAGIC_BLUE}
}

monster.elements = {
    {type = COMBAT_PHYSICALDAMAGE, percent = 20},
    {type = COMBAT_ENERGYDAMAGE, percent = 20},
    {type = COMBAT_EARTHDAMAGE, percent = 20},
    {type = COMBAT_FIREDAMAGE, percent = 100},
    {type = COMBAT_LIFEDRAIN, percent = 0},
    {type = COMBAT_MANADRAIN, percent = 0},
    {type = COMBAT_DROWNDAMAGE, percent = 0},
    {type = COMBAT_ICEDAMAGE, percent = -10},
    {type = COMBAT_HOLYDAMAGE, percent = -10},
    {type = COMBAT_DEATHDAMAGE, percent = 20}
}

monster.immunities = {
    {type = "paralyze", condition = true},
    {type = "outfit", condition = false},
    {type = "invisible", condition = true},
    {type = "bleed", condition = false}
}

mType:register(monster)

💡 Best Practices

1. Organize Your Scripts

data/scripts/
├── actions/
│   ├── quests/
│   ├── tools/
│   └── doors/
├── movements/
│   ├── teleports/
│   └── tiles/
└── talkactions/
    ├── player/
    └── admin/

2. Use Descriptive Names

-- ❌ Bad
local a = Action()

-- ✅ Good
local questChestAction = Action()

3. Comment Your Code

local action = Action()

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    -- Check if player already completed the quest
    if player:getStorageValue(10001) == 1 then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The chest is empty.")
        return true
    end
    
    -- Give reward and mark quest as completed
    player:addItem(2160, 10)
    player:setStorageValue(10001, 1)
    return true
end

action:uid(5001)
action:register()

4. Combine Multiple Events in One File

-- Quest system with multiple event types
local questAction = Action()
local questMovement = MoveEvent()

function questAction.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    -- Action logic
    return true
end

function questMovement.onStepIn(creature, item, position, fromPosition)
    -- Movement logic
    return true
end

questAction:uid(5001)
questAction:register()

questMovement:aid(5002)
questMovement:register()

🔗 Additional Resources


Made with 💜 by Mateuzkl

Clone this wiki locally