DIR Return Create A Forum - Home
---------------------------------------------------------
The World Of Perisno
HTML https://perisno.createaforum.com
---------------------------------------------------------
*****************************************************
DIR Return to: Everything About Modding
*****************************************************
#Post#: 1780--------------------------------------------------
Making A Quest (By Lumos)
By: Michadr Date: August 30, 2013, 7:35 pm
---------------------------------------------------------
Walking home about (EDIT: two hours and a) half an hour ago, I
was thinking about writing a full-fledged tutorial about
implementing a quest along with additions to nearly every file,
but then I realised two things. First, this is a massive
overhead and requires more time than I can spare at the moment.
Secondly, I'm too short on beer for such an endeavour.
So, I decided to simply explain how triggers and mission
templates work, and I'll do it in detail and with examples. So
let's get started, shall we?
- (Simple) Triggers:
There are two types of triggers in M&B: Regular triggers and
simple triggers. We'll begin with the simple triggers, because
they're generally smaller and easier to understand.
Before that though, what is a trigger? A trigger is a block of
code that executes multiple times as the game progresses. It's
called a trigger, because it fires - that's how it's called when
the time comes for the trigger to do its job.
Simple triggers:
Simple triggers are generally found in module_simple_triggers.py
and despite being... well, simpler, they're not to be
underestimated. Let's have a look at a simple trigger and then
explain what does what:
[code](24, [
(troop_add_gold, "trp_player", 100),
]),[/code]
This is rather simple, isn't it? Every 24 hours, this trigger
will give 100 gold to the player. Yes, it makes no sense, but it
works. And that's basically how every simple trigger operates.
Here's the structure: (check_interval, [ operations_block ]),
This is shared by each and every simple trigger, including the
gold-giving one we've got above. Now let's look at the
components:
check_interval is how frequently this trigger will be checked,
measured in in-game hours. Using 24 will make the trigger fire
once every day, using 1 will make it fire 24 times per day.
NB: Using 0 as a check interval will make the trigger fire once
every frame, i.e. a lot of times per second, dependent on the
user's frame rate. Such triggers are potential performance hogs,
so be cautious when using them.
The operations block is what happens when the trigger fires, and
can contain whatever codes you want to place in it. Yes, checks
are allowed. For an example, we can enhance our aforementioned
code by doing this:
[code](24, [
# (try_begin), # We don't need try blocks right now
(lt, "$player_honor", 0),
(troop_add_gold, "trp_player", 100),
# (try_end),
]),[/code]
Now it will give a hundred gold per day to the player, if he/she
is dishonourable. It makes even less sense now, but whatever. As
you can see, a trigger can fail with no problems whatsoever,
however be careful when not wrapping statements in try_ blocks.
Of course, it's always safer to use the try blocks, just in
case.
Same But Fixed
[spoiler][code](24, [
(try_begin), # Better safe than sorry
(lt, "$player_honor", 0), # Make sure your indentation is always
correct!
(troop_add_gold, "trp_player", 100), # Tabs don't look so huge
in the text editors, only here on the forum.
(try_end),
]),[/code][/spoiler]
Right, so we've got the basics covered now. There are more
things you need to know though. Simple triggers are also used in
other places, such as on items and scene props, but with special
conditions, for an example:
[code]["flintlock_pistol", "Flintlock Pistol",
[("flintlock_pistol",0)],
itp_type_pistol|itp_merchandise|itp_primary,
itcf_shoot_pistol|itcf_reload_pistol, 230,
weight(1.5)|difficulty(0)|spd_rtng(38)|shoot_speed(160)|thrust_d
amage(10,
pierce)|max_ammo(1)|accuracy(65), imodbits_none, [ # A comma is
always followed by a spacebar, as your friendly schoolbooks
teach you
(ti_on_weapon_attack, [
(play_sound,"snd_pistol_shot"),
(position_move_x, pos1,27),
(position_move_y, pos1,36),
(particle_system_burst, "psys_pistol_smoke", pos1, 15) # The
last line in a field can remain without a comma, but be careful
when adding more lines later
])]],[/code]
[code] ("catapult_destructible",
sokf_moveable|sokf_show_hit_point_bar|sokf_destructible,
"Catapult", "bo_Catapult", [
(ti_on_init_scene_prop, [
(store_trigger_param_1, ":instance_no"),
(scene_prop_set_hit_points, ":instance_no", 1600),
]),
# Other triggers omitted for clarity
]),[/code]
As you can see, these two examples use respectively
ti_on_weapon_attack and ti_on_init_scene_prop. which are
hard-coded conditions passed by the engine at the right times. A
list of all such "special" check intervals can be found in
header_triggers.py.
This about sums up everything related to simple triggers, now
let's move on to...
Triggers:
The regular triggers, called only "triggers" are a bit more
complex and versatile than their simpler counterparts, but are
largely the same. These triggers are more important, however,
because they are used in the mission templates, which we'll talk
about afterwards.
Let's examine the structure of a trigger:
(check_interval, delay_interval, re-arm_inverval, [
conditions_block], [ operations_block ]),
As you can see, there're more fields present here than there
were above. Now let's examine them one by one.
- The check_interval is absolutely the same as before - how
often the trigger fires;
- The delay_interval is how much the consequences should be
delayed once the conditions are true;
- The re-arm_interval is how long should the trigger remain
inactive once the consequences are applied;
- The conditions_block contains the code that needs to be
checked in order to allow for the consequences to fire;
- The consequences_block is where the majority of your ocde is
usually located, it will be executed once the conditions are
evaluated to true and the delay_interval wears off.
Now, how would our senseless trigger from above look like?
[code](24, 0, 0, [], [
(try_begin),
(lt, "$player_honor", 0),
(troop_add_gold, "trp_player", 100),
(try_end),
]),[/code]
This is perfectly valid, but it's frankly kind of stupid if
we're going to use a regular trigger. Let's buff up the rewards
and rework the code a bit in order to make use of the systems
that this trigger allows us:
Much Better:
[code](24, 6, 48, [
(lt, "$player_honor", 0),
], [
(troop_add_gold, "trp_player", 400),
]),[/code]
Now, every 24 hours this trigger will evaluate the player's
honour. If it's below zero, six hours will pass, keeping the
player on edge (okay, not really), and then he or she will be
awarded four hundred gold. However, the trigger will not fire
for the next 48 hours, which kind of compensates for the greater
reward.
Yes, the trigger is still dumb, but serves as a nice example.
Regular triggers also have their special conditions, most of
which related to missions - ti_before_mission_start,
ti_on_agent_spawn and so on, once again listed in
header_triggers.py.
NB: Local variables are not transferred between conditions and
consequences. In other words, you'll need to store a variable
again to modify it in the consequences if you've already stored
it to check it.
NB: When dealing with triggers with special conditions, you are
likely to need to check their parameters using the
(store_trigger_param) operation. Consult header_triggers.py for
a list of parameters for each trigger.
I hope you've learned something. If you haven't, I'm sorry.
Now, we'll take a look at...
- Mission templates
First of all, you need to understand one simple concept - that a
mission template is largely a template to be used for similar
missions. A mission is any occasion that you're controlling your
character in. Mission templates provide different functions
through trigger usage and are pretty important, being the second
required component of a mission (the other one is a scene for
them ission to play in). They are usually accessed through a
menu with the (set_jump_mission, "mt_mission_template"),
(jump_to_scene, "scn_selected_scene"), (change_screen_mission),
set of commands.
A mission template's structure is a lot more complex than a
trigger, and contains lists of objects such as triggers. Let us
examine a mission template:
(mission_template_id, flags, type, useless_text, [ spawn_records
], [ triggers ]),
- The mission_template_id is what you're going to be referencing
this mission template with;
- The flags are just that - flags. Look in
header_mission_templates.py for a list of flags, leave it a
zero when you've not got any special conditions;
- The type is a special value that is usually -1, you're
unlikely to need to change it;
- The useless_text is a mission template's description, which
serves absolutely no purpose. I guess you could use it to
describe stuff for yourself, but I believe it only takes up
space;
- The spawn_records is a list of spawn points and conditions for
them;
- The triggers is a list of triggers, much like the ones we
discussed above.
First of all, we have to look through the structure of each
spawn record and see what they're made of.
(entry_id, spawn_flags, alter_flags, ai_flags, num_troops, [
equipment_list ]),
- The entry_id is the spawn point number which the troops from
this record will use;
- The spawn_flags are flags that dictate something about the
spawning - like mtef_team_0 and so on;
- The alter_flags are flags that say what equipment should the
agents NOT spawn with - for an example, af_override_horse or
af_override_all
- The ai_flags are flags that will give the AI agents spawned
from this record a specific behavour. Usually used with
aif_start_alarmed;
- The num_troops is how many agents should spawn from this
record;
- The equipment_list is a maximum of items that will be added to
the agent's inventory, eight at max. Overriding all and adding
the pilgrim disguise in the equipment_list results in the player
being disguised while "fooling the town guards".
Let's make a dummy mission template, so we can talk about stuff.
Consider this:
[code]("useless_template",0,-1, "Nothing to see here", [
(0, mtef_visitor_source|mtef_team_0, af_override_fullhelm,
aif_start_alarmed, 1, []),
(1, mtef_visitor_source|mtef_team_1, af_override_fullhelm,
aif_start_alarmed, 1, []),
], [
(ti_on_agent_spawn, 0, 0, [], [
(display_message, "@Agent spawned"),
]),
(ti_on_agent_killed_or_wounded, 0, 0, [], [
(store_trigger_param, ":dead_agent_id", 1),
(agent_get_troop_id, ":troop", ":dead_agent_id"),
(str_store_troop_name, s1, ":troop"),
(display_message, "@{s1} died a gruesome death. Or was knocked
unconsious, we're not checking that."),
]),
# Health regen for the player
(1, 0, 0, [
(neg|main_hero_fallen),
(get_player_agent_no, ":agent"),
(agent_is_alive, ":agent"),
], [
(get_player_agent_no, ":agent"), # Local variables are not
transferred between conditions and consequences
(store_agent_hit_points, ":health", ":agent", 1),
(val_add, ":health", 1),
(agent_set_hit_points, ":agent", ":health", 1),
]),
]),[/code]
In this mission template, two agents on two different teams are
going to spawn, and they'll spawn without helmets. Upon their
spawning, two "Agent spawned" messages will roll out, and when
one dies you will be notified by another message. On top of all
that, you will regenerate health wat a rate of 1 HP/second
(which is quite overpowered).
As you can see, this mission template contains two spawn records
and three triggers. The mtef_visitor_source will allow for
agents to spawn through the usage of (modify_visitors_at_site,
"scn_current_scene"), (set_visitor, entry_point_number,
"trp_whatever"), while mtef_scene_source... I've got no idea
what it does. Feel free to experiment.
The triggers here are pretty self-explanatory, I reckon.
And given that I've wasted too much time already on this, I'll
have to wrap it up here. The best advice I could give you is to
LOOK at the files and see how things are already done. Then
you'll understand how to do them yourself.
Hope this helps. If it doesn't, I won't write another one.
*****************************************************