Map Editor/Scripting

Fantasy Tales Online uses Javascript (JS) as its scripting engine. All the JS executing happens server-side to manipulate the character objects. The synchronization is handled by the API. This is the same flavor of JS used by web browsers without the DOM & Web APIs. Any JS language document should be applicable as long as their don't reference any DOM or Web APIs.

The API
The best resources for scripting help is by consulting the API documentation:

Rhino Scripting API: http://varium.fantasytalesonline.com/sandbox/rhino/

If you have an issue with the API please notify the staff on the forums.

Examples
The best way to get familiar with a new scripting API to look at some example. Here's some examples for some common behavior:

Spawn Location
Use the special 'prescript_spawn_location' to instruct where new characters should spawn.

function prescript_spawn_location { return ["Arden1,3,0", 1200, 1696]; }

Quest Chatter
Simple chatter based on the quest progression.

function on_click(npc, player) { if (player.quest_isWorkingOnQuest("The Blobs")) { npc.sendMessageToPlayer(player, "Thank you kindly for helping me! the supplies are at the Winterfront village general store."); } else if (player.quest_hasCompletedQuest("The Blobs")) { npc.sendMessageToPlayer(player, "You're an amazing person! Thank you for helping me."); } else { npc.sendMessageToPlayer(player, "Ooh! a visitor... how wonderful. Please stay for a good long while. I haven't seen anyone in months."); } }

Completing a quest objective
Here we have the script for a bed with an objective to set your spawn point. Clicking this object will update your spawn point and if you're working on a quest it will set the objective completed by setting the quest cookie. Note that this quest has a quest objective set to look for 'set_spawn' in the quest editor.

function on_click(npc, player) { if ( player.quest_isWorkingOnQuest("Take A Rest")) { player.quest_storeCookie("Take A Rest", "set_spawn", true); }   player.sendMessageToPlayer(player, "ZzZzZ"); player.sendMessage("Spawn Location Set!"); player.addHP(10000); player.setSpawnLocation(npc.getMap.getMapName, player.getX, player.getY); }

Waypoint Chatter
This is the script for an NPC character with various waypoints. This will call 'on_waypoint' and is used to script chat messages at each waypoint.



function on_load (npc){ npc.setBlockable(false); }

function on_playermapenter (npc, player){ npc.setBlockable(false); }

function on_click (npc, player) { npc.sendMessageToPlayer(player, "Argh! *snort* Raaah!"); }

function on_waypoint (npc, waypoint) { if (waypoint == "stop1" ) { npc.sendMessageToMap("Wolf. Kill. Fire. Yum!"); npc.waitAtNextWaypoint(5000); }

if (waypoint == "stop2" ) { npc.sendMessageToMap("*snort* FIZZLE... Boombala! *snort*"); npc.waitAtNextWaypoint(5000); }

if (waypoint == "stop3" ) { npc.sendMessageToMap("BBQ Troll. Yum!"); npc.waitAtNextWaypoint(5000); }

if (waypoint == "stop4" ) { npc.sendMessageToMap("ARGH!"); npc.waitAtNextWaypoint(5000); }

if (waypoint == "stop5" ) { npc.sendMessageToMap("Meep! *snort*"); npc.waitAtNextWaypoint(5000); } }

Object visibility based on quest status
This script will check if the quest status each time a players enter the map and decide how that player should view this object. In this case this object will be hidden only for the receiving player by calling |npc.setAlpha(alpha, player);|.



function prescript_cavein_rocks(npc) { var self = this; var questName = "Cave In Rescue Part 2"; var cookieName = "cavein_pick"; this.updateState = function(npc, player) { var isShow = !player.quest_hasReturnedQuest(questName); var alpha = isShow ? 100 : 0;           npc.setAlpha(alpha, player); };           this.on_playermapenter = function (npc, player) { self.updateState(npc, player); };           this.on_quest_statuschange = function (npc, player, quest) { if (quest == questName) { self.updateState(npc, player); }            };    }

and the rect object provides the collisions for the warp only if you have completed the quest to clear the rocks:

function on_rectobj_load(map,x,y,w,h,rect) { prescript_winterfrontmines_link(map, x, y, w, h); }

function prescript_winterfrontmines_link(map, x, y, w, h) { var self = this; var questName = "Cave In Rescue Part 2";

map.collisionFunction(x, y, w, h, function(player) {       if (player.quest_hasReturnedQuest(questName)) {            player.warpToOtherMap("Arden2,0,-1", 400, 1088);        }    }, null); }

Puzzle block
Here's a puzzle block that will block players from moving through it. It is activated when the right trigger event is sent to this object by the switch.

var spriteBlocked = "image:data/sprite/dungeon/puzzleBlocker.png,64,64,0,0,0"; var spriteUnblocked = "image:data/sprite/dungeon/puzzleBlocker.png,64,64,0,64,0"; var isBlockedAtStart = true;

prescript_dungeon_blocker(npc, spriteBlocked, spriteUnblocked, isBlockedAtStart);

function prescript_dungeon_blocker(npc, spriteBlocked, spriteUnblocked, isBlockedAtStart) { var currentBlockedState = isBlockedAtStart; npc.on_trigger(npc.getSharedVar("trigger"), function {       currentBlockedState = !currentBlockedState;

if (currentBlockedState) { npc.setSprite([spriteBlocked]); } else { npc.setSprite([spriteUnblocked]); }       npc.setBlockable(currentBlockedState); });   npc.setBlockable(currentBlockedState);    npc.setTargetable(false); }

Custom Tutorial
If you want to have a tutorial for your server you can use this model:

function prescript_tutorial(player) { var questName1 = "We Need Help"; var questName2 = "Test Your New Weapon";

function getWeaponInInventory(player) { var items = player.getInventoryList; for (var i = 0; i < items.length; i++) { if (items[i].isWeapon) { return items[i]; }       }        return null; }

function checkNewStage(stageId) { var stage = player.getCookie("tutorial_stage"); if (stageId != stage && stageId == 6) { player.playSound("pickup1"); player.storeCookie("tutorial_stage", stageId); }   }

// Make sure everything is completed in order if (!player.getCookie("tutorial_objective_start") && !player.quest_hasReceivedQuest(questName1)) { checkNewStage(0); player.tutorialHighlightMap("Tutorial:\nTo move use the WASD keys or the mouse. Talk to Hew for your first quest.", function {            player.playSound("pickup1");            player.storeCookie("tutorial_objective_start", true);            prescript_tutorial(player);        }); } else if (!player.quest_hasReceivedQuest(questName1)) { checkNewStage(1); player.tutorialHighlightArrow("right", "Arden1,3,0", 1200, 1640, "Talk to Hew"); } else if (!player.getCookie("tutorial_objective")) { checkNewStage(2); player.tutorialHighlightObjective("Your quest objective(s) will show up here.", function {            player.playSound("pickup1");            player.storeCookie("tutorial_objective", true);            prescript_tutorial(player);        });

....

} else if (!player.quest_hasReturnedQuest(questName2)) { checkNewStage(8); player.tutorialHighlightArrow("down", "Arden0,2,0", 1095, 1711); player.tutorialHighlightArrow("down", "Arden0,3,0", 1557, 269); player.tutorialHighlightArrow("right", "Arden0,3,0", 1800, 552); player.tutorialHighlightArrow("down", "Arden1,3,0", 339, 727); player.tutorialHighlightArrow("right", "Arden1,3,0", 722, 928, "Return the quest"); player.tutorialHighlightArrow("up", "Arden1,3,0", 964, 706); player.tutorialHighlightArrow("up", "Arden1,3,1", 843, 327, "Talk to Folmar"); } else if (!player.getCookie("tutorial_done")) { checkNewStage(9); player.tutorialHighlightMap("Now you're on your own. Follow the map and head to the mines to the east for some real combat or do some quests in town first.", function {            player.storeCookie("tutorial_done", true);            player.playSound("pickup1");            player.setAchievement("ACHIEVEMENT_EDUCATED");            checkNewStage(10);            prescript_tutorial(player);        }); }

// Mark the current set of tutorial entry to set atomically player.tutorialHighlightReady; }