FOnline: Reloaded

Development => The Workshop => Topic started by: Slowhand on December 25, 2015, 01:01:46 pm

Title: Basic Development Tutorial for FOnline. (Reloaded)
Post by: Slowhand on December 25, 2015, 01:01:46 pm
This is a brief development tutorial for developing FOnline. In the tutorial I use the FOnline: Reloaded version of the SDK, but it should be fully compatible with the original SDK.
(Note: As we found out, everything is not compatible, especially scripts. If you plan to follow this tutorial, make it easier on yourself and use the Reloaded version, see first step for install instructions)

Another copy of this tutorial can be found at FoDev.net (http://fodev.net/forum/index.php/topic,29789.0.html) as well.

Basic Development Tutorial for FOnline.

Contents:

To do list:
Title: 1. Downloading and setting up the Development Kit. (FOnline: Reloaded)
Post by: Slowhand on December 25, 2015, 01:02:17 pm
1. Downloading and setting up the Development Kit.

Title: 2. Modifying existing maps with the Mapper.
Post by: Slowhand on December 25, 2015, 01:02:54 pm
2. Modifying existing maps with the Mapper.

Another easy step, is to modify and existing map and check the effects.
Title: 3. Adding new locations to the world, using the World editor.
Post by: Slowhand on December 25, 2015, 01:03:20 pm
3. Adding new locations to the world, using the World editor.

This isn't hard as well, but here u can mess things up pretty easy if you click around while experimenting.

Now, time to place this map on the world.
Test what you have done. Make sure you delete the World saves, start server, launch client and with a new char you should see the new tutorial location nearby.
Title: 4. Adding dialogues for NPC's
Post by: Slowhand on December 25, 2015, 01:03:48 pm
4. Adding dialogues for NPC's

Okay, let's continue, developing our newbie tutorial. This robot shall give usefull information for newbies who never played any FOnline games and are used to the original Fallout1/2/3/New Vegas etc games. So, our robot shall teach the newbies how to be self sufficient, how to gather resources, harvest, make tents etc. Ofc if they want to. It should introduce the old quality of questing/talking to NPC's where the NPC reacted to the Players stats, actions etc.

Step by step:

I made a little bit more complex dialog for my robot, feel free to understand it and try it. Basically the robot detect the players Intellect and greets him accordingly. The player can gather some extra information if he has high perception. The successfull scenario ends in the robot giving the player a task to gather him flint, so the robot can teach him how to craft a tool.

Here is how it looks. (click on images for normal size)

(http://i.imgur.com/Adj06h3.png?1)
Title: 5. Creating a two map zone.
Post by: Slowhand on December 25, 2015, 01:04:10 pm
5. Creating a two map zone.

Step by step:

Here are my maps:

(http://i.imgur.com/C3NzxeW.png?1)

(http://i.imgur.com/xd4TsMc.png?1)
Title: 6. Adding monsters, loots and some basic (pre-written) scripts.
Post by: Slowhand on December 25, 2015, 01:04:33 pm
6. Adding monsters, loots and some basic (pre-written) scripts.

So, next in line is, adding some monsters to the map, with some basic script. We will not make scripts, only use some default ones.
The first thing you might notice, is that if you place a critter on the map, for example a Mole Rat, it will stay idle and do nothing, unless attacked. In order to make a Critter be agressive or semi-agressive, it will need some scripts. There are some good basic scripts in the Klamath map (Rats/Geckos), we will use those. (The are in the scripts files not in the map ofc, but you can access them by checking Klamath map with Mapper)

So, step by step:

Adding some loot for the mobs.

The result should look like this in Mapper:

(http://i.imgur.com/TwOQ2hg.png)

Adding agressive mobs usually does not fullfill all of the quest writers desires. So our next step is, to add some mobs, that do not move/attack, until they are triggered to do so. For this, we will also make a new dialog. The molerat will talk to the player, and the player will have some options how to engage it. The choise "I will exterminate you and all your vermints." will trigger all mobs who see the player to attack him, otherwise the player can leave peacefully or take on the molerat in a man versus rat duel.

Step by step:
Note: Use Demand or Result to add scripts to dialogs. The one we added, was in the script file named dialog and the scripts function name was r_Alert. We will do more complicated scripts later on, but if you want, feel free to study some of the script files.

(http://i.imgur.com/rFTJKvp.png)
Title: 7. More on dialogs - Tracking quest progress.
Post by: Slowhand on December 25, 2015, 01:05:11 pm
7. More on dialogs - Tracking quest progress

The next important thing with dialogs is, to track a given quests progress. For this, we will need to create a game variable, track it through the dialog and through other actions of the player as well.

Step by step:

This is how it looks at my end:

(http://i.imgur.com/Tx7crIG.png)

On the above picture, the tutorial robot will check if the quest varialble (q_turo_prog) is 10, which means that the player has reached a point in the conversation with the robot, where the robot will ask him, to gather some flint and use his fixboy to make a primitive tool.


(http://i.imgur.com/6J30TpE.png)

On the above picture, the tutorial robot will ask the player to make a primitive tool and to mark the progress so far, will set the quest variable to 10, and exit the dialog. Next time when the player talks to the robot, it will jump to dialog 9 where the robot asks if the task is done. Here the robot checks if the player has a primitive tool on him, and will progress further if so.

This was only a part of the Tutor Robot quest, but enough to point out the example, how to progress using a quest variable. Ofc, other dialogues would lead to incrementing or setting back to quest variable to other values, and because of the Predialogue installation Answer Demands, the right dialog will be selected for the current progress of the quest.
Title: 8. Creating and using some basic scripts: Scenery scripts.
Post by: Slowhand on December 25, 2015, 01:05:56 pm
8. Creating and using some basic scripts: Scenery scripts.

A quest complexity will usually require us to use scripts. At this point we will dive into one of the most simple scripts, the Scenery scripts, or scripts associated with scenery objects on the map. For this we will need a Scenery object on our map and a new script we want to call when the user interract with it.

Step by step:
Code: [Select]
//  Includes some definitions
#include "_macros.fos"

//  Function signature for player interactions with Scenery objects
bool s_RefillRobot(Critter& player, Scenery& robot, int skill, Item@ item)
{
// Retrieve the variable used by the quest to mark it's progress, plus error handling
GameVar@ questVar = GetLocalVar(LVAR_q_turo_prog, player.Id);
if(valid(questVar))
{
// is quest still in progress and before repairing the robot
if (questVar < 30)
{
//  Check if repair skill is used on the robot
if(skill == SK_REPAIR)
{
player.Say(SAY_NETMSG, "You don't think it can be done without some small energy cells.");
return true;
}
// Check if science skill is used on the robot
if(skill == SK_SCIENCE)
{
player.Say(SAY_NETMSG, "The robot is running very low on energy.");
return true;
}
// Check if the player used a Small Energy Cell on the robot.
if(valid(item))
{
if(item.GetProtoId() == PID_SMALL_ENERGY_CELL)
{
player.Say(SAY_NETMSG, "You replace one of the energy cells of the robot.");
// Set the quest progress
questVar = 30;
// Remove the Small Energy Cells used.
item.SetCount(item.GetCount() - 1);
item.Update();
return true;
}
}
}
else
{
if(skill == SK_REPAIR || skill == SK_SCIENCE)
{
player.Say(SAY_NETMSG, "You already replaced the energy source of this robot, there is nothing more you can do here.");
return true;
}
}
}
return false;
}
Title: 9. Creating new quest location from dialog.
Post by: Slowhand on December 25, 2015, 01:06:24 pm
9. Creating new quest location from dialog.

After receiving a quest from an NPC, the NPC might in some cases mark a location on the player's map. This location will be known only to the player, and a red dot on the world map will appear. Once the player finished the quest (talks with the questgiver and succedes), the location will disappear.

Step by step:

Here is the basic dialog, it's crude but only for demonstration of usage:

(http://i.imgur.com/wKpB9DX.png)

Here is the script that will spawn a location on the map and will delete it afterwards when triggered.

Code: [Select]
#include "utils_h.fos"
#include "worldmap_h.fos"
#include "npc_planes_h.fos"
#include "entire.fos"
#include "_colors.fos"
 
 
// function called as request from dialog to spawn quest location
void r_SpawnLoc(Critter& player, Critter@ npc)
{
// roll X,Y value of WM (World Map) zone index, in this case from 3x2 zones area near Hub
uint zoneX = Random(28, 31);   
uint zoneY = Random(39, 40);   

// get X,Y value of quest location position on WM in zone we picked above
uint   wx = zoneX * __GlobalMapZoneLength;          //get zone X,Y start value
uint   wy = zoneY * __GlobalMapZoneLength;
wx += Random(0, __GlobalMapZoneLength);             //add random value from 0 to zone size
wy += Random(0, __GlobalMapZoneLength);

// you can add more map locations here, and select one randomly
array<uint16> locationIds = { 91 };
uint num = locationIds.length;

// pick random encounter map, if only one map, then only one will be selected
uint16        locPid = locationIds[Random(0, num - 1)];

// create quest location
Critter@[] crits = { player };
int           loc = CreateLocation(locPid, wx, wy, crits);
if(loc == 0)
return;
// makes the location visible to the player
player.SetKnownLoc(true, loc);

// change location color on World Map
Location@ location = GetLocation(loc);
location.Color = COLOR_RED;
location.Update();

// set TB combat mode if needed
if(player.Mode[MODE_DEFAULT_COMBAT] == COMBAT_MODE_TURN_BASED)
{
SetTurnBasedAvailability(location);
}

// set location id to quest lvar (used when you need to delete location)
GameVar@  locidv = GetLocalVar(LVAR_q_gen_locid, player.Id);
locidv = loc;

// player can die and come back, but we will have to delete the location later
location.AutoGarbage = false;
}
 
// dialog function used in request to delete quest location (after player report finishing the quest)
void r_DeleteLoc(Critter& player, Critter@ npc)
{
GameVar@ var = GetLocalVar(LVAR_q_gen_locid, player.Id);
DeleteLocation(var.GetValue());
}
Title: 10. Accessing a private location from a public location scenery. (Ladder)
Post by: Slowhand on December 25, 2015, 01:06:56 pm
Coming soonish..

I did not solve this yet, but it seems that map_bosbunker.fos has some nice example, how to make the script for a map, so that the player has to attach a rope to an elevator shaft and climb down on it.

The map for it is, q_bos_oldbunker_level2.fomap, here the elevator shaft does not have a rope attached, but the script "map_bosbunker@s_Shaft" is set up.
Title: 11. Setting up the scripting environment.
Post by: Slowhand on December 25, 2015, 01:07:21 pm
11. Setting up the scripting environment.

Scripting from notepad after second day, is no fun. Before touching more of the scripting topics, our first task is to set up a scripting environment, which helps and speeds up our work. We will configure an IDE (Interactive Development Environment) for this, which wil replace the text editor we used to write and read scripts. There are many free IDE's out there, I tried a few, and here are the results:


Step by step - Configuring Code Blocks for scripting FOnline: Reloaded

This is how my IDE looks like when scripting:

(http://i.imgur.com/yhKtusi.png)
Title: 12. Understanding a full quest script. (LA Boneyard dog quest)
Post by: Slowhand on December 25, 2015, 01:07:50 pm
12. Understanding a full quest script. (LA Boneyard dog quest)


Before writing scripts, understanding a few of them could come handy. The repeatable Boneyard dogs quest is perfect for this. It includes new location spawning from script, adding mobs to it and some triggers to run when the quest objective is fulfilled.

I will try to go very detailed on this one, pointing at every feature, or good to know stuff. This might be a bit too basic level, but maybe someone needs it. Having a little bit of C/C++ or any programming experience helps a lot here, but I will assume that you have none. This will be a very long trip, if you get lost, just skip, practice a bit using the previous or next material and come back later.

Very basic general (with examples of  programming info for those who are not proficient with it, or tried to understand the scripts but failed. If you are proficient in proramming head down to1 3. Navigation in the source code for FOnline development specific info.


Here is the script file, with included comments to explain. Copy it and open with Codeblocks.

Code: [Select]
/**< Credits and description */
//
// FOnline: 2238
// Rotators
//
// quest_la_dogs.fos
//

/**< These are includes. They tell the compiler where to look after code not defined in this file. */
#include "quest_killmobs_h.fos"
#include "worldmap_h.fos"
#include "utils_h.fos"
#include "npc_planes_h.fos"
#include "entire.fos"

/**< Same as include, I don't know why this is separated from the other includes. This line includes only one function, not all of them. */
import uint GetZonesWithFlag(uint flag, array<IZone@>@ zones) from "worldmap";

/**< Definitions to help to understand and use the code easier. */
#define ALL_KILLED            (2)

#define IDLE_NORMAL           (3000)
#define DIALOG                (9471)
#define PID                   (82)
// the time after dog group is added to the zone it's been removed from
#define GROUP_RESPAWN_TIME    (REAL_HOUR(Random(20, 30)))

// check if there is some group of dog roaming on the worldmap near LA
/**< Checks the map for roaming dogs. It's use has be shortcut to always return true, that is probably to optimize?. Useless currently.*/
bool d_CheckDogs(Critter& player, Critter@ npc)
{
    return true;
    /*IZone@[] zones;
       uint num = GetZonesWithFlag(FLAG_LA_Surroundings, zones);
       for(uint i = 0; i < num; i++)
            if(zones[i].GetBaseQuantity(GROUP_Dog) > 0) return true;
       return false;*/
}

/**< Opposite of checkdogs, always returns false. Useless currently. */
bool d_NoDogs(Critter& player, Critter@ npc)
{
    return !d_CheckDogs(player, npc);
}

/**< Spawns the location, sets up the critters, events, timers, initializes everything */
void r_SpawnLoc(Critter& player, Critter@ npc)
{
    /**< Get collection of zones near LA Boneyard. */
    array<IZone@> zones;
    uint num = GetZonesWithFlag(FLAG_LA_Surroundings, zones);
    array<IZone@> dogzones;
    /**< Cycle through the zones around LA Boneyard and add the zones that contain dogs to another collection called dogzones */
    for(uint i = 0; i < num; i++)
        if(zones[i].GetBaseQuantity(GROUP_Dog) > 0)
            dogzones.insertLast(zones[i]);
    /**< If no zones with dogs in it found, exit without spawning location.*/
    if(dogzones.length() == 0)
        return;

    /**< Get a random zone from the dogzones. */
    IZone@ zone = random_from_array(dogzones);

    /**< Generate random World Map coordinates. */
    uint   wx = zone.GetX() * __GlobalMapZoneLength;
    uint   wy = zone.GetY() * __GlobalMapZoneLength;
    wx += Random(0, __GlobalMapZoneLength);
    wy += Random(0, __GlobalMapZoneLength);

    /**< Select a random location on the World Map zone. (square)*/
    array<uint16> pids;
    num = zone.GetLocationPids(pids);
    uint16        locPid = pids[Random(0, num - 1)];

    /**< Create the location, add the player */
    Critter@[] crits = { player };
    int           loc = CreateLocation(locPid, wx, wy, crits);
    if(loc == 0)
        return;

    /**< Make the location visible for the player */
    player.SetKnownLoc(true, loc);

    /**< Get the game variable q_la_dogs_locid and store the location id associated with the players quest. */
    GameVar@  locidv = GetLocalVar(LVAR_q_la_dogs_locid, player.Id);

    /**< GameVar is a handle to the _la_dogs_locid game variable, it's value will be stored there when this function terminates. */
    locidv = loc;

    /**< Get the location, allow turn based mode in it, and disable auto garbage, this way the player can revisit the map. */
    Location@ location = GetLocation(loc);
    if(player.Mode[MODE_DEFAULT_COMBAT] == COMBAT_MODE_TURN_BASED)
        SetTurnBasedAvailability(location);
    location.AutoGarbage = false;

    /**< Get the maps of the location and initialize them with default values. */
    array<Map@> maps;
    uint        mapcount = location.GetMaps(maps);
    for(uint c = 0; c < mapcount; c++)
    {
        maps[c].SetScript(null);
        maps[c].SetEvent(MAP_EVENT_IN_CRITTER, null);
        maps[c].SetEvent(MAP_EVENT_CRITTER_DEAD, null);
    }

    /**< Set the player to be the owner of the first map of the location. */
    Map@ map = GetLocation(loc).GetMapByIndex(0);
    SetOwnerId(map, player.Id);
    /**< Repeat until dogs are spawned successfully on the map. */
    // spawn dogz
    bool spawned = false;
    while(!spawned)
    {
        array<Entire> entires;
        ParseEntires(map, entires, 0);

        /**< Get a random Entire to spawn the player to, when he enters the map. */
        Entire@ ent = random_from_array(entires);

        /**< Get a hex position at a random angle and distance from the player to spawn the dogs to. */
        uint16 hx = ent.HexX;
        uint16 hy = ent.HexY;
        map.GetHexCoord(ent.HexX, ent.HexY, hx, hy, Random(0, 359), Random(10, 40));
        for(uint i = 0, j = Random(7, 12); i < j; i++)
        {
            int[] params =
            {
                ST_DIALOG_ID, DIALOG
            };

            /**< Creates a dog and adds it to the map. Assignes critter_init function to dogs, explained below, at definition.*/
            Critter@ doggie = map.AddNpc(PID, hx, hy, Random(0, 5), params, null, "quest_la_dogs@critter_init");

            /**< If creating and adding at least one dog to the map succeded, then the main cycle stops. */
            if(valid(doggie))
            {
                spawned = true;
            }
        }
    }

    /**< Makes sure the location is salvaged after 12 hours, if the player does not finish quest until then.
        It will also set q_la_dogs variable to 3 (need to check dialog tree, probably reseting quest) */
    SetQuestGarbager(12 * 60, player.Id, loc, LVAR_q_la_dogs, 3);
}

/**< Deletes the location the player killed the dogs at. */
void r_DeleteLoc(Critter& player, Critter@ npc)
{
    GameVar@ var = GetLocalVar(LVAR_q_la_dogs_locid, player.Id);
    DeleteLocation(var.GetValue());
}

/**< Critters need an initialization function to be functional, well, this is it. */
void critter_init(Critter& cr, bool firstTime)
{
    /**< Disables replication for killed dogs. */
    cr.StatBase[ST_REPLICATION_TIME] = REPLICATION_DELETE;
    /**< A bunch of event handler: each will set the function to be called when the event happens. */
    cr.SetEvent(CRITTER_EVENT_DEAD, "_DogDead");
    cr.SetEvent(CRITTER_EVENT_ATTACKED, "_DogAttacked");
    cr.SetEvent(CRITTER_EVENT_MESSAGE, "_DogOnMessage");
    cr.SetEvent(CRITTER_EVENT_IDLE, "_DogIdle");
    cr.SetEvent(CRITTER_EVENT_SHOW_CRITTER, "_DogShowCritter");

    /**< I don't understand what this does, if someone finds out, please tell me as well. */
    _CritSetExtMode(cr, MODE_EXT_MOB);
}

/**< Function to run when mob is idle. It move from time to time, but only a few hexes.*/
void _DogIdle(Critter& mob)
{
    MobIdle(mob);
}

/**< This is called when a new critter is in sight of the dog. */
void _DogShowCritter(Critter& mob, Critter& showCrit)
{
    /**< If the seen creature is not the same type, it will attack it. */
    MobShowCritter(mob, showCrit);
}

/**< Checks if all dogs are dead on the map. */
void _DogDead(Critter& cr, Critter@ killer)
{
    uint16[] pids = { cr.GetProtoId() };
    Map@ map = cr.GetMap();
    if(MobsDead(map, pids))
    {
        GameVar@ var = GetLocalVar(LVAR_q_la_dogs, GetOwnerId(map));
        var = ALL_KILLED;

        /**< These parts have been severed by someone, it seems that some useless code remained. */
        // remove one dog group from given zone
        IZone@ zone = GetZone(cr.WorldX, cr.WorldY);
        // zone.ChangeQuantity(GROUP_Dog, -1);
        // spawn event to restore the doggie
        uint[] values = { cr.WorldX, cr.WorldY };
        CreateTimeEvent(AFTER(GROUP_RESPAWN_TIME), "e_SpawnDogGroup", values, true);
    }
}

/**< Useless code, it was used for extra reality, but someone changed the core part. */
uint e_SpawnDogGroup(array<uint>@ values)
{
    IZone@ zone = GetZone(values[0], values[1]);
    // zone.ChangeQuantity(GROUP_Dog, 1);
    return 0;
}

/**< How dog handles when it is attacked. It will send a message on the map to everyone that he is attacked including itself. (Yeah, I know, lol) */
bool _DogAttacked(Critter& cr, Critter& attacker)
{
    return MobAttacked(cr, attacker);
}

/**< If the message is that a dog is attacked, it will attack the attacker. */
void _DogOnMessage(Critter& cr, Critter& fromCr, int message, int value)
{
    MobOnMessage(cr, fromCr, message, value);
}

I have described how to look after specification from the FOnline API and how to find definitions using codeblocks. Use these to navigate throw the code I copied it. It will contain most of the important stuff in comments.

Also, try to make as much as you can out on your own, the code seems to be correct, except at the parts I commented otherwise. I left in the old comments as well, the new ones start like this: "/**<".

The biggest dificulty this script and others presents is that many things are passes through an ID, a numeric value to identify and entity, or message type, etc. Once you get used to it, and you should if you want to write scripts, understanding scripts will go fast and easy. :)
Title: 13. A simple kill target critter quest.
Post by: Slowhand on December 25, 2015, 01:08:24 pm
13. A simple kill target critter quest.
Title: 14. Using the "Say" menu from dialogues. (Riddle mini quest.)
Post by: Slowhand on December 25, 2015, 01:08:49 pm
14. Using the "Say" menu from dialogues. (Riddle mini quest.)

It is quite simple actually, all you need to do, is at a dialogue you created, link the function to call at the dialogue, instead of results or demands like before. The signature will change to the following: uint dlg_Name(Critter& player, Critter@ npc, string@ say).

When the client accesses the node which you linked the say-script, it will run the script immediately, will not wait for the client to press "Say", so the first line of the code needs to be something that checks if the user entered something using say. (check code example for it)

The return value of the script function will tell which dialogue node to access next. 0 stands for the same dialogue node, running the script again, -1 stands for exiting the dialogue, and different positive values will try to find and access a dialogue node with that number. In our example, that was the 4.

Here is the code, using the previous tutorials, try to install it, do not forget to add the script to the script lists, the dialogue you created to the dialogue scripts and linking the NPC dialog ID to the ID's you gave to your dialogue at _dialogs.fos.

Script file: tut_say_dialogue.fos (http://pastebin.com/EKndgvPJ)
Dialogue file: tut_say_dialogue.fodlg (http://pastebin.com/hpmtiFLP)

This is how my dialogue file looks like, showing where to add/edit the script functions called (left click on node 3):

(http://i.imgur.com/vse7NkL.png)
Title: 15. Modifying base mechanics - Only the easy parts.
Post by: Slowhand on January 07, 2016, 06:10:33 pm
15. Modifying base mechanics - Only the easy parts.
Title: 15.1 Modifying lock pick cooldown mechanic.
Post by: Slowhand on January 07, 2016, 06:11:00 pm
15.1 Modifying lock pick cooldown mechanic.

(Using Reloaded SDK source, version 2.)

This will be an example, how to spice up lock picking mechanic a bit, changing the value of cooldown from a static, to a dynamic value based on the player skill, tools used, lock difficulty and the random roll.


The script file of interest is "lockers.fos". Generally the usage of skills is defined in "main.fos", but this one there is empty.

I do not know how to explain this minor change in basic tutorial level, here is what the old code did:

The new mechanic:

Points of interest:

1. If you didn't so far, make sure you understand the CLAMP(x, min, max) macro:

What it does, is makes sure that a value is between boundaries, like the value of CLAMP(x, 1, 95) will be "x" if that is between 1 and 95, else if "x" is higher than 95, then the value will be 95 or if "x" is lower than 1 the value will be 1. The CLAMP macro is defined using the ternary operator "?:" (actually two of them embedded in each other), which has the following formula: "logical condition ? value_if_true : value_if_false", meaning that if the condition is true, the first value will be returned, else the last. It might be a bit confusing, because of the excessive usage of this operator, but you'll get used to it.

2. Random(x, y) function:

This will generate a random number between two values, the numbers are decimal. Most common use case is: int roll =Random(0, 100); In this case the "roll" variable will be used somewhere against a skill, critical or to hit check.

3. LOCKPICK_TIMEOUT(cr):

This a macro, it's a leftover of some old code, and it has been left there to note the old structure of the code. It is still used, but it is overridden to a static value, which makes the extra "cr" parameter useless. I keep using it and let the owner of the server who use this tutorial code to dispose of it if they want to. Basically, instead of this macro we could have used simply a value.

4. REAL_SECOND(s):

The LOCKPICK_TIMEOUT macro refers to this, and what this macro does transforms real time value into game value, hence the name "real".

5. Locker's parameters:

Lockers as simply Items, you can find a lot in mapper under container, and have 3 parameters regarding locking, which you can modify also in mapper.


The new code fragment to replace the old parts with in the script file: lockers.fos (http://pastebin.com/v26Qz8Db)

Simply replace the old part with the new part, I did not copy the whole file into pastebin, but left enough context ( a few extra lines) and comments, so you can find where to copy paste from.
Title: Re: Basic Development Tutorial for FOnline. (Reloaded)
Post by: Henry on January 21, 2017, 02:56:34 am
This guide and the next one (advanced) are now being added to the wiki (http://www.fonline-reloaded.net/wiki/index.php?title=A_Development_Tutorial). I believe we all owe Slowhand a lot of thanks for this. It's EXACTLY what this community needs for possible junior developers to get started in a strong way. It must have taken a TON of time to do all of this, and i'm very pleased to finally get it on the wiki in the Guides (http://www.fonline-reloaded.net/wiki/index.php?title=Guides) section.

If you like this game and are thinking about writing some new content for it (stories/quests, maps, NPC dialogues and more), Slowhand has made it a lot easier for you to get started!

SPECIAL THANKS TO SLOWHAND!
Title: Re: 2. Modifying existing maps with the Mapper.
Post by: cthulchu on August 04, 2017, 02:20:21 am
Slowhand, I want to say that this is an amazing tutorial. You did a great job, granting a lot of cool content to Reloaded. Thank you a lot!


I'll leave here some notes while working on my setup if you don't mind. You can incorporate some of these notes into the manual if you want.

1.
  • Launch Mapper and load the map of The Hub
  • Press enter after mapper started to get the console.
  • Type: "~hub" - to load the map.
  • Press F8 to disable mouse scrolling. (This can be very annoying, since even if the Mapper does not have focus, it will try to scroll while you are working with other apps.
  • Press Enter again, when the command line is empty, so the command line disappears, now you can use the arrow keys to scroll around.
Make sure you don't alt+tab when the console is opened. It's bugged. You won't be able to close the console if you do... Will have to relaunch the mapper. Oh and you'll lose the progress with no way to save it :)

OR you can just type Alt and it'll unfreeze the console :) hehe :)

(http://i.imgur.com/FaklEFD.jpg)

2.
For some fooken reason Clean.bat doesn't clean the save folder.
Here's how the modified version should look like:
(http://i.imgur.com/0BLzJvA.jpg)
Added this string:
del save\*.fo

3.

And... It crashed. Stopped working. And I lost all progress. Don't forget to save your map periodically.
Quote
Save the map: "^tut_map1"

Happens every time I scroll through normal items. Have to hack around with ctrl+scroll

4.
I found some super helpful things that would help a lot during map building. Looks like the editor is a lot more functional than I thought. It's just it doesn't have nice documentation and GUI config menu system. But when you get used to it... You can create freaking awesome things relatively quickly.

WOWOWOW! I found a cool manual. Here it is:

Quote
To load a saved map, type "~<map name>" in the console.
TO save a map, type "^<map name> [/text] [/nopack]". "/text" to save it in text format, "/nopack" to save it in unpacked version.

Other commands, preceded with "*":
 new      create a new map;
 unload      unload the current map;
 scripts   list of scripts;
 size <w> <h>   set a new map size;
 dupl      search for items with the same pids, placed on the same hexes;
 scroll      search for scroll blockes, around which there are less than two other scroll blockers;
 pidpos <pid>   search for all items with a given pid;
 hex <hx> <hy>   search for all objects on a given hex.

To run a script function, use "#". Executed function must be of prototype "string FuncName(string)", string argument is passed from a console, and function results will be displayed in the message box. Default module is "main", to execute a function from another module use "@" (for example, "#module@MyFunc").
Example:
In console, "#MyFunc Hello wo";
In script, "string MyFunc(string my) { return my + "rld!"; }";
Result in the message box: "Result: Hello world!".

Scripts are placed in the "/data" directory. List of loaded scripts is in scripts.cfg. The main script is mapper_main.fos, which contains all reserved functions, and basic list of API functions.

The mapper does not support FO and FO2 map formats, as well as older FOnline map formats.

On the main panel, there is a range of buttons that group the objects by their type.
Below these buttons, there's a panel with buttons toggling the display of items in the map view.
On the right, there are buttons that determine which objects will be selected by clicking and dragging the mouse.
Other buttons:
Fast - displays list of often used items;
Ign - Ignore, shows ignored objects that are not rendered on the map;
Inv - Inventory, shows objects inside a container or inside critters' inventory;
Lst - List, shows all loaded maps and allows to switch between them.

Upon selecting an object, an option window will appear allowing to edit some of it's properties (marked green; white are not editable).
To add an object to Ignored objects list, click it with Ctrl on the object selection panel.
To add an object to the container or inventory, click it with Alt on the object selection panel.
To put an object in a critter's slot, click the item in it's inventory with Shift.

To apply a change in the object properties window to a range of selected objects (of the same type), click To All button.

To remove selected object(s), press Del.

To change direction of a critter, use middle mouse button.

To add a range of objects to already selected objects, hold Ctrl.

To change zoom, use mouse wheel.

To speed up scrolling of objects in the panel, use the following:
Shift - one page,
Ctrl - 100 elements,
Alt - 1000 elements.

To play animation of a selected critter, use "@" command with codes of desired animations. For instance, to play animations of movement, and then using, type "@abal" (case is ignored, as well as whitespaces). If no critter is selected, all critters on the map will play the animation. For information on animation codes, see http://modguide.nma-fallout.com/#Graphics011

To move a critter, hold down Shift and click the desired position.

Hotkeys:
F1: Enable/Disable display of items.
F2: Enable/Disable display of sceneries.
F3: Enable/Disable display of walls.
F4: Enable/Disable display of critter.
F5: Enable/Disable display of tiles.
F6: Enable/Disable display of frequently used objects.
F7: Hide/Show the main panel.
Shift + F7: Fix the position of the main panel (enabled by default).
F8: Enable/Disable scrolling with mouse.
F9: Hide/Show the object properties window.
Shift + F9: Fix the position of the object properties window (disabled by default).
F11: Set rain.
Shift + Escape: Exit the mapper.
Del: Delete the selected objects.
Ctrl + X: Cut objects.
Ctrl + C: Copy objects.
Ctrl + V: Paste objects.
Ctrl + A: Select all.
Ctrl + S: Enable/Disable ignoring of scroll blockers.
Ctrl + B: Show impassable hexes. Red are impassable, and not shootable through. Green are impassable, shootable through.
Ctrl + M: Display NPC information over their heads (this has few modes of display).
Ctrl + L: Save the log to a txt file.
Tab: Change the type of objects selection (diamond or rectangle).
+: Change time by 1 hour.
-: Change time by -1 hour.

Special Entires:
0 - default;
240 - starting position for a player who logged for a first time;
241 - starting position for players on a map with NoLogOff flag.
242 - replication.
243 - car entry point.
245 - vertibird entry point.
246 - boat entry point.

Okay, I think this list of tips is enough for a few days of map making for now.

5.

You have made a map, cleared the server cache, added the map into mapper, created a location, added the map to it, added it to the WM and... The server crashes, saying your map "doesn't exist"?

Someone didn't bother to set up adequate error reporting. Check your entry points and their codes. Your map must be visitable.

PPS

why does it add smileys... weird... Cuz I didn't post this smiley ->
Title: Re: Basic Development Tutorial for FOnline. (Reloaded)
Post by: Slowhand on August 04, 2017, 01:53:01 pm
Thanks for the correction, I fixed the typos.

You get a smiley after free :), because everyone is happy for free stuff and BBCodes know this.
Title: Re: Basic Development Tutorial for FOnline. (Reloaded)
Post by: cthulchu on August 05, 2017, 05:53:16 pm
I have a few questions after making a few maps with the mapper.


I have some more in-depth questions regarding server mechanics, but I'll ask them on fodev.

PS

Now we need an ultimate manual that would unite all the existing manuals. I have read five different manuals on mapping and each has brought something unique. And mapping is not the complex thing. This manual though is the best to start with.
Title: Re: Basic Development Tutorial for FOnline. (Reloaded)
Post by: Slowhand on August 05, 2017, 06:23:46 pm
2. Why do we have all these empty fallout2 maps in the maps folder?

Because no one cleaned them up? I don't know, maybe Reloaded team knows. I wouldn't concern with details that are not part of your objective, whatever those are, simply because too much time can be wasted without finding sure answer.

9. How do I copy the answers in the dialogue editor? Or how do I use answers for dialogue A from a dialogue B... Looks like I'm missing something here. I could add a technical answer to A that would send the branch to dialogue B, but it's not very elegant

If the tool has missing features, you can use the files themselves, they are text based. Just open and copy paste.

10. I have some more in-depth questions regarding server mechanics, but I'll ask them on fodev.

Well, if it is related to FOnline: Reloaded, you should ask here first, as fodev is not based on Reloaded source.

Now we need an ultimate manual that would unite all the existing manuals. I have read five different manuals on mapping and each has brought something unique. And mapping is not the complex thing. This manual though is the best to start with.

Go ahead :)
Title: Re: 7. More on dialogs - Tracking quest progress.
Post by: cthulchu on August 07, 2017, 10:37:28 pm
  •    $   3   1   q_turo   0   0   1000   0
  • **********
  •    TuRo progress.
  • **********
the software fails to add vars. Could you please describe the significance of the flag property.

I mean, I saw the headers, but they are unclear:

Id   Type   Name      Start   Min   Max   Flags
**************************************************************************************
   $   1   0   car_id   0   0   0   4

What values of Flags mean what? I need a local map variable for an SE. Need it to die when a person leaves the map.

And the checkboxes. What's the meaning of them?

And what's the significance of having variables in the NPC or player? Do I understand it correctly that variables on an NPC can be accessed by other players while variable on a player can be accessed by other NPCs.

Therefore, if my "quest" is very local and affects only 1 npc and one player, it doesn't matter whether I choose NPC or player, I just have to be consistent. Am I understanding it correctly?

Would be nice to update that part of the manual.

And what is recheck?

And I need to do a hack. I've seen this logics somewhere: You talk to an npc, do something and then the same npc answers you with strings.
So what I intuitively did is:
(http://i.imgur.com/raZpCnw.jpg)
if the var is 0, the player never interacted with the npc.
1 - interacted once, but never got to the end.
2 - went to the end and now has to get strings.
And here's what I get
(http://i.imgur.com/7EcKmGm.jpg)

How do I get the strings?

Thanks!
Title: Re: 7. More on dialogs - Tracking quest progress.
Post by: Slowhand on August 08, 2017, 08:36:47 am
  •    $   3   1   q_turo   0   0   1000   0
  • **********
  •    TuRo progress.
  • **********
the software fails to add vars. Could you please describe the significance of the flag property.

I mean, I saw the headers, but they are unclear:

Id   Type   Name      Start   Min   Max   Flags
**************************************************************************************
   $   1   0   car_id   0   0   0   4

What values of Flags mean what? I need a local map variable for an SE. Need it to die when a person leaves the map.

And the checkboxes. What's the meaning of them?

I don't know. The way I found out what I did, was to experiment and ask on fodev.net/irc. Didn't got exact specification. If you find out, let me know, or write a page on it.


And what's the significance of having variables in the NPC or player? Do I understand it correctly that variables on an NPC can be accessed by other players while variable on a player can be accessed by other NPCs.

Therefore, if my "quest" is very local and affects only 1 npc and one player, it doesn't matter whether I choose NPC or player, I just have to be consistent. Am I understanding it correctly?

Would be nice to update that part of the manual.


Try this to understand it better: Art of Dialogs (http://fodev.net/files/sdk/ArtOfDialogs.pdf)

And what is recheck?

As check again? Make sure you did everything exactly as it you were supposed to. If you miss something it's hard to find out what and very frustrating. We need a better SDK.
Ah that recheck. Maybe I read something about it, but forgot, sorry.


And I need to do a hack. I've seen this logics somewhere: You talk to an npc, do something and then the same npc answers you with strings.
So what I intuitively did is:
(http://i.imgur.com/raZpCnw.jpg)
if the var is 0, the player never interacted with the npc.
1 - interacted once, but never got to the end.
2 - went to the end and now has to get strings.
And here's what I get
(http://i.imgur.com/7EcKmGm.jpg)

How do I get the strings?

No clue about the strings, I even forgot there are strings in the dialog editor. I think I never used them.

Thanks!

You are welcome. To find out stuff, you need to experiment a lot and have even more patience. Basically it's learning by examples, just check other dialogues, how they use the strings or whatever you want to find out. I remember checking the Banker guy to find out how to use the 'ask' option in dialogues. If you played the game, that helps a lot, as you will know which NPC could have a script of dialog that you can check for examples.

If you find useful stuff, not covered, write a post about it.
Title: Re: Basic Development Tutorial for FOnline. (Reloaded)
Post by: cthulchu on August 08, 2017, 04:17:59 pm
yeah, I just hoped you'd know.

And I believe some of the older npc's dialogues might be scripted without use of dialogues since the whole dialogueing system is a shortcut for scripts. Not sure when it was created and started being used. Maybe before hardcore work with dialogues it's better to start scripting to understand the real structure better. I'll see.
Title: Re: Basic Development Tutorial for FOnline. (Reloaded)
Post by: cthulchu on August 09, 2017, 02:17:20 am
Hey Slowhand, haven't you forgot to register your .fos in the section #8? I get this error:

Code: [Select]
[52:890] ProtoMap::BindSceneryScript - Map<pid<131>, name<se_oasis_cave_cth>>, Can't bind scenery function<s_Disassemble> in module<se_oasis.fos>. Scenery hexX<209>, hexY<101>
here's my .fos:

Code: [Select]
//  Includes some definitions
#include "_macros.fos"
#include "_dialogs.fos"
#include "_maps.fos"
#include "elevators_h.fos"
#include "factions_h.fos"
#include "mapdata_h.fos"
#include "messages_h.fos"
//  Function signature for player interactions with Scenery objects
bool s_Disassemble(Critter& player, Scenery& robot)
{

return RunDialog(player,robot.HexX, robot.HexY, 31338,false);
}

Don't mind the includes.
It compiles:
Code: [Select]
F:\Games\Fonline\Mapping\Server\scripts>ascompiler.exe se_oasis.fos -p prep.txt -d __SERVERs
Compiling se_oasis.fos ...
Success.
Time: 82,0483 ms.

F:\Games\Fonline\Mapping\Server\scripts>

I suspect I have to register my script either in scripts.cfg or in _scripts.fos I tried and it didn't work. Though maybe I did something wrong.