It is currently 01 Nov 2024, 02:34

WELCOME TO SIMUSCAPE!


Please Sign in or Register to enable all features, remove restrictions and gain additional access!
For information on how to bypass the CAPTCHA or to contact Team Simuscape, Continue Here!


Post a new topicPost a reply Page 1 of 1   [ 11 posts ]
Author Message
PostPosted: 31 Aug 2012, 23:57 
Artists Guild Team

Joined: 02 Mar 2012, 12:10
Posts: 75
Object animation and other fun in m4nfo

Introduction

m4nfo is a "high-level language" frontend for generating so-called "nfo byte code", compiling "human-readable" source code into plain nfo, which m4nfo then feds into grfcodec (probably together with appropriate graphics) to generate newGRFs for TTDPatch and OTTD.

m4nfo pursues a two-fold approach: whilst retaining the efficiency characteristic of plain nfo, with the prior-ranking aim to generate small and fast code, it introduces higher abstraction levels where it makes sense for the programmer, considering established newGRF coding habits.

To achieve these goals, m4nfo has been implemented as a set of small 'modules' instead as a large monolithic application. There are separate modules for trains, stations, objects, ..., reflecting the fact that most of the time a newGRF only deals with one of TTD's 'features'. (In fact, even outside the scope of m4nfo, it is recommended behaviour to not mix different features into one newGRF.)

This approach doesn't only keep m4nfo small and lightweight (M4: 143kB, module 'trains': 40kB), it also allows to use identical identifiers and names for same methods on different features, keeping its name space small, and avoiding redundant labeling.

Taking into account the modular structure of m4nfo, it is very easy to add extra user-supplied functions or even whole modules. This can be done either 'on-the-fly', placing functions directly inside of the user's source files; or it could be done by adding additional include files. It is even possible to pre-compile user-supplied modules, or - rather exotic - include sections of plain nfo code directly into m4nfo source files.

In addition, m4nfo natively includes a macro processor suitable for private customizing or templating applications (indeed, m4nfo is written in M4, a very efficient macro processor itself), so it won't need any external tools, like CPP for macro usage or artificially crafted extra Python layers, resulting in bloated installations.

There are pre-compiled modules, needing no m4nfo system include files and running even slightly faster, for the diverse features, which might be easily used manually or from a makefile script.

In conclusion, m4nfo is best used for large projects because of its unrivalled speed and code efficiency, the latter only depending on the experience of the coder, because there is no additional overhead introduced by the compiling process. Last but not least, the ability of m4nfo to handle distributed source files is best suited when using a build-management tool like 'Make', to constitute an efficient programming environment for the really large projects.

Attachment:
PSPad_full.png
PSPad_full.png [ 34.64 KiB | Viewed 10933 times ]

For more detailed information see the m4nfo User Manual and Technical Report.


Tutorial

"newGRFs with 'objects' - animation and other fun in m4nfo"

Part 1 - simple animation

The first example will demonstrate a very basic animation which would be constantly looping and does not use any triggers. It is based on one of the mole lights from the MariCo set. The object in question would use its animation to generate a "character" (to help the sailors to discern this very light from others). This is achieved by splitting the (animated) "light" from the base object (the "lighthouse" in the further course) and thus showing two different objects in a continuous loop, either only the lighthouse or the combination of lighthouse and light.

First thing to do would be to define the object at all. In m4nfo, this is done by use of function "defineobject()", and here it is:
Code:
defineobject(_LIGHTS,
   classid(MC01)
   classname(moles)
   objectname(molelight)
   climate(TEMPERATE, ARCTIC, TROPIC)
   size(1,1)
   price(80)
   timeframe(1.1.1880 .. 1.1.2050)
   flags(NOBUILDONLAND, HASANIMATION)
   callbacks(CB_TEXT)
   anim_info(10, LOOP)
   anim_speed(5)
   anim_triggers(BUILT)
   buildingheight(2)
   numviews(4)
)

As can be seen from the function body, this object´s ID is "_LIGHTS" (some number), its class-ID is set to "MC01" (to place it correctly in the object building menu), its classname is set to the string referenced by the text identifier "moles", and its objectname to the string referenced by "molelight" (both left out in this tutorial, see the user manual). It´ll be available in temperate, arctic and sub-tropical climates, it´s of size 1*1, it may be built for a certain price inside a timeframe from january 1st, 1880 to january 1st 2050, must be built on water, has animation, and uses the "additional text" callback.

W/r to animation, it uses 10 animation frames in a loop, with animation speed of "5", and the only animation trigger would be the building of this particular object.

Other than this, it has a height of "2" (i.e. 2 * 8px), and comes in 4 "views", i.e. 4 different graphical representations to be chosen when being built.

Now, some real (graphics) sprites have to be prepared, this is done in m4nfo in the following way:
Code:
spriteblock(
     ...
// 8 lighthouses green (LIGREEN[1 .. 8])
  set(
    sprite(mole.pcx 10 175 09 22 8 -1 -16)
  )
  set(
    sprite(mole.pcx 20 175 09 26 6 0 -20)
  )
  set(
    sprite(mole.pcx 28 175 09 27 8 -1 -21)
  )
  set(
    sprite(mole.pcx 38 175 09 25 12 -3 -19)
  )
  set(
    sprite(mole.pcx 52 175 09 24 10 -2 -18)
  )
  set(
    sprite(mole.pcx 64 175 09 23 4 1 -17)
  )
  set(
    sprite(mole.pcx 70 175 09 27 8 -1 -21)
  )
  set(
    sprite(mole.pcx 80 175 09 23 6 0 -17)
  )
  ...
// animated green light (ANIMGREEN)
  set(
    sprite(mole.pcx 171 175 09 3 2 0 0) // green
  )
  ...
)

As can be seen, we´re going to use 8 different lighthouse sprites, please note that these aren´t the "views", but our code will randomly display those 8 different lights per view.

In addition, we need a green light (ANIMGREEN) for the animation. And exactly that´s what the whole procedure is for: TTD has no animated green colour!

Now, the needed sprites having been defined in a "spriteblock" function, we need means to compose the object from its individual sprites into a "tile". This is done by function "spriteset":

Attachment:
animtut.png
animtut.png [ 11.44 KiB | Viewed 10933 times ]

Code:
//------------------------------------------------------------------
// sprite sets with 8 lighthouses green
//------------------------------------------------------------------

// #1
def(50) spriteset(
   set(normal(WATER), normal(2), xyz(0,0,0), dxdydz(16,16,6)) // mole
   set(normal(LIGREEN1), xyz(1,5,6), dxdydz(8,8,16)) // light
)

def(51) spriteset(
   set(normal(WATER), normal(2), xyz(0,0,0), dxdydz(16,16,6)) // mole
   set(normal(LIGREEN1), xyz(1,5,6), dxdydz(8,8,16)) // light
   set(normal(ANIMGREEN), xyoff(3,2)) // animated light (green)
)

def(22) anim_frame(
   ref(50) if(4, 9) // dark
   ref(51) else
)

This is the code for one (#1) of the 8 objects, they´re different w/r to the sprite for the light and the animation sequence.

In m4nfo, assembling sprites into tiles is done by function "spriteset" which supports all the subtle ways a tile might be defined in TTD, e.g. sprites defining their own 3D "bounding box", or so-called "child sprites", sharing their parents bounding box, recoloured or transparent sprites, etc.

At first, let´s take a look on the un-animated light (the "base"):
Code:
def(50) spriteset(
   set(normal(WATER), normal(2), xyz(0,0,0), dxdydz(16,16,6)) // mole
   set(normal(LIGREEN1), xyz(1,5,6), dxdydz(8,8,16)) // light
)

First of all, note the function "def()". Unlike other "high-level" languages, m4nfo handles plain nfo´s "c-IDs" (by which the chain of control is set up) manually. In this way, the introduction of an inefficient garbage collection is avoided, and the user may benefit from "re-usage of c-IDs" which is a very handy feature in plain nfo, especially because of the existing limit of 255 c-IDs. (O/c, it´s always possible to "label" the defs in m4nfo, and re-use them at a later time by that label. OTOH, using plain numbers is especially clear in a local context, rather than using endlessly long identifiers ...)

So, let´s take a look on that "def(50)". It defines a spriteset consisting of two "sets", both defining a so-called "parent sprite". The first parent sprite (the mole where the lighthouse will be placed on) uses TTD´s "water" sprite as its groundtile and real sprite number "2" (the mole piece) from the spriteblock above for its building tile. It defines a bounding box of 16 * 16 pixels with a height of 6 pixels, which completely covers the ground tile, because its origin is placed at coordinate (0,0), see picture.

The second sprite is the lighthouse to be placed on top of the mole piece, using sprite LIGREEN1 from the sprite block above. This set defines a smaller bounding box of 8 * 8 pixels, located at (1,5) at a height of 6 pixels, i.e. right on top of the first bounding box.

Both sprites are handled "normally", that´s why the function "normal()" is used for them. (There are special ways to handle real sprites in TTD/m4nfo, e.g. using company colour, two company colours or recolouring, those would be handled by different functions than "normal()".)

Well, that´s our lighthouse tile now, consisting of a water ground sprite, a mole piece, and a lighthouse building.

Now, we need the same thing with an additional green light, and here it is:
Code:
def(51) spriteset(
   set(normal(WATER), normal(2), xyz(0,0,0), dxdydz(16,16,6)) // mole
   set(normal(LIGREEN1), xyz(1,5,6), dxdydz(8,8,16)) // light
   set(normal(ANIMGREEN), xyoff(3,2)) // animated light (green)
)

First two sets are identical to those from def(50), but there´s now a third one. This one is a so-called "child sprite", i.e. it has no bounding box, but it does share the bounding box of the previos sprite, i.e. that of the lighthouse. The xyoff() parameter specifies the offset of sprite ANIMGREEN with regards to LIGREEN1: there´s an offset in x of 3 pixels, and an offset in y (height) of 2 pixels.

The deep reason to split up the sprites in this particular way lies with the problem of displaying the tile in 4 directions (x front, x back, y front, y back) which would need four times the number of sprites, while we´re able now to "shift" the light sprite w/r to the mole sprite according to the direction of the tile, just by setting the xyz() coordinates in a clever way.

Well, let´s move on now to the animation step. This is simply done by
Code:
def(22) anim_frame(
   ref(50) if(4, 9) // dark
   ref(51) else
)

Are you still remembering that our animation was defined for 10 frames? Well, function anim_frame() references def(50) on the 4th and the 9th frame, and def(51) for all the other frames, generating the neat effect of switching off the light on frames 4 and 9.

Needless to say that the other 7 light versions are using different "characters" (animation sequences), helping the sailors to keep the lights apart.

So, in a next step, we need to randomize those 8 light versions:
Code:
def(14) randomrel(CONSTRUCT, 1, ref(22), ref(23), ref(24), ref(25), ref(26), ref(27), ref(28), ref(29)) // green

This function defines yet another def(14) randomizing defs 22, 23, ..., 29 on construction, i.e. after being built, they´d stay the same. (Object don´t have any more random triggers.)

Unfortunately, now it gets a bit lengthy, because we need those three other directions (and then, we´d need everything again for the 8 red lights, doh!), but we´ll skip it here, because it doesn´t introduce anything new, except from different sprites and x/y-offsets.

Anyway, eventually we´re ending at this part of the code:
Code:
//------------------------------------------------------------------
// mole straight in x with lighthouse green
//------------------------------------------------------------------

// mole in x, find end of straight line
def(40) objinfo_water(pos(-1,0), // back
   ref(14) if(ENDMOLE)     // lighthouse
   ref(0) else
)

def(41) objinfo_water(pos(1,0), // front
   ref(16) if(ENDMOLE)    // lighthouse
   ref(40) else
)

def(42) objinfo_slope(
   ref(10) if(WEST+SOUTH) // slope back
   ref(12) if(NORTH+EAST) // slope front
   ref(41) else         // flat, check for end tile
)

def(50) callback(
   animcontrol(0) if(CB_ACONTROL) // start animation
   ref(42) else
)

Remember, nfo (and m4nfo) goes *backwards* from its "action3" (m4nfo: "makeobject()" function), so let´s take a look first on def(50). Its function "callback()" checks for callback CB_ACONTROL (animation control) active, and in case starts our animation at frame 0. In case, it´s not a CB_ACONTROL, the function branches off to def(42).

Now, function def(42) checks for a potential slope (e.g., when building this object on a coast tile). Because this part of the code is only concerned with x-direction, it´s sufficient to check for one back (WEST+SOUTH) or one front slope (NORTH+EAST). On such slopes, no mole lights can be built, so these branches are left for special mole building on coast tiles (defs 10 and 11).

So, only if there´s no back or front slope tile, we carry on with def 41. Fortunately, we don´t have to check for a flat (water or land) tile, because we can only built moles on water. So, next thing to check would be for a mole´s free end (i.e., a clean water tile), because we´d like to allow the building of a mole light only at the mole´s end. This is done by checking the neighbour tiles in front and back of the mole piece for being "water" by using function "objinfo_water()": We first check for a possible water tile in front of our mole piece ("pos(1,0)"), and then at the back ("pos(-1,0)"). Depending on that direction (+1 in x == front, or -1 in x == back) we´re branching off to our animated mole light, i.e. our def(14) from above gets references for the back direction.

If no water tile is found ("ref(0)"), a normal straight mole piece in x-direction is being built.

Now, again we have left out three other possibilities, namely green light in y-direction and red lights in x- and y-direction. Imagine, these got defs 51, 52, and 53, we now come to an end of the whole exercise:
Code:
//------------------------------------------------------------------
// make 4 "views" (green light in x and y, red light in x and y)
//------------------------------------------------------------------

// graphics
def(60) getviews(
   ref(50) if(0) // green light in x (this is our test case)
   ref(51) if(1) // green light in y
   ref(52) if(2) // red light in x
   ref(53) else  // red light in y
)

// menu
def(61) getviews(
   ref(36) if(0) // green light in x
   ref(37) if(1) // green light in y
   ref(38) if(2) // red light in x
   ref(39) else  // red light in y
)

def(62) callback(
   reftxtcb(warn_water) if(CB_TEXT)
   ref(61) else
)

makeobject(_LIGHTS,
   link(ref(62),MENU)
   default(ref(60))
)

First of all, def(60) sets up 4 "views" which can be chosen from the building menu. def(50) is the green light in x which we have come along with today, and obviously, the other three variants are linked to the remaining three views.

Now, the same is done with the menu sprites, which had been set up elsewhere (defs 36 .. 39).

So, let´s finish our object: When in the building menu (MENU), control is transfered to def(62) checking for an additional CB_TEXT callback (displaying some help text) or to the menu sprites; and if not in the building menu, control is handed directly to def(60).

Yeah, this was the first animation tutorial, an easy one. Congratulations you came through! Soon, we´ll moving on to something more challenging. Stay tuned.

regards
Michael


Top
 Offline Profile  
 
PostPosted: 15 Jan 2016, 10:52 
Artists Guild Team

Joined: 02 Mar 2012, 12:10
Posts: 75
I have added another tutorial here: http://www.ttdpatch.de/grfspecs/m4nfoManual/ASLTut.html

This one is about m4nfo´s implementation of OTTD´s "advanced sprite layouts" (dynamically customising sprite layouts), and might be interesting in case anyone here is still working on newobject sets.

The tutorial´s example is on "objects adjusting automatically to neighbour objects", making use of some clever m4nfo features.

regards
Michael


Top
 Offline Profile  
 
PostPosted: 16 Jan 2016, 06:28 
Moderator
User avatar

Joined: 29 Feb 2012, 00:55
Posts: 1770
Location: Hellas
Wally and SAC will love this.


Top
 Offline Profile  
 
PostPosted: 16 Jan 2016, 08:45 
Master Mentor
User avatar

Joined: 27 Feb 2012, 22:45
Posts: 1880
Location: Canada
athanasios wrote:
Wally and SAC will love this.
I had worked this out some time ago in NFO for a yet to be completed project. It was a mind-bender to say the least. I'll not be doing that again any time soon. Good to see that Michael has it working for m4nfo and I recommend anybody wanting to avail themselves of this feature, they should learn m4nfo and do it themselves. 8-)

_________________
Visit SimuSchool - Tutorials, Questions and Answers
TTDPatch Nightlies Downloads are back
Thrive


Top
 Offline Profile  
 
PostPosted: 16 Jan 2016, 12:07 
Artists Guild Team

Joined: 02 Mar 2012, 12:10
Posts: 75
wallyweb wrote:
I had worked this out some time ago in NFO for a yet to be completed project. It was a mind-bender to say the least. I'll not be doing that again any time soon. [...]


As I wrote (elsewhere) the feature is either buggy or its documentation is flawed, and it´s ill-formed at least for use with newobjects. Since I don´t think 'the developers' (tm) would fix this anytime soon, I have tried to mask these shortcomings in the m4nfo implementation as good as it was possible.

regards
Michael


Top
 Offline Profile  
 
PostPosted: 17 Jan 2016, 01:29 
Moderator
User avatar

Joined: 29 Feb 2012, 00:55
Posts: 1770
Location: Hellas
A bit off topic: I wonder why you didn't enter the 'developers club' so far.


Top
 Offline Profile  
 
PostPosted: 17 Jan 2016, 12:52 
Artists Guild Team

Joined: 02 Mar 2012, 12:10
Posts: 75
athanasios wrote:
I wonder why you didn't enter the 'developers club' so far.


I haven´t even been a "developer" back in TTDPatch times. This was a deliberate decision, since I wanted to spent my time on developing game additions (newGRFs). And every time I had a good idea for the game itself, it had been implemented by Josef, Csaba or Oskar. So ...

regards
Michael


Top
 Offline Profile  
 
PostPosted: 18 Jan 2016, 10:51 
Simuscape Admin
User avatar

Joined: 15 Sep 2011, 09:25
Posts: 3034
Location: Sweden
The good old days Michael, the good old days... :)

I haven't had a closer look at this project of yours Michael, all I saw was a whole bunch of code and I was like...

:shock:

:o

:|

But I do love making graphics though... :lol:

_________________
Image

Simuscape - A world of its own;
SimuTalk | Visual Studio | INFRA Diary

INFRA - Chose Your Destination;
INFRA Projects | INFRA Downloads


Top
 Offline Profile  
 
PostPosted: 19 Jan 2016, 06:02 
Moderator
User avatar

Joined: 29 Feb 2012, 00:55
Posts: 1770
Location: Hellas
Hmm, I am already dizzy with all that code, now if we added to the animated objects seasonal changes (snow, water/mud in tropic climate, rough seas) how would it look like? Better not even think about it. :cucko: :oops:


Top
 Offline Profile  
 
PostPosted: 30 Jun 2016, 14:49 
Artists Guild Team

Joined: 02 Mar 2012, 12:10
Posts: 75
I´ve added another example about the use of OTTD´s "advanced sprite layout" in m4nfo for stations, in the aforementioned tutorial.

regards
Michael


Top
 Offline Profile  
 
PostPosted: 19 May 2017, 13:56 
Artists Guild Team

Joined: 02 Mar 2012, 12:10
Posts: 75
I´ve added yet another tutorial for m4nfo. This time it´s about usage of OTTD´s "stacked sprites" feature for vehicles.

Composing vehicles from multiple sprites:
http://www.ttdpatch.de/grfspecs/m4nfoMa ... ckTut.html

regards
Michael


Top
 Offline Profile  
 
Display posts from previous:  Sort by  
Post a new topicPost a reply Page 1 of 1   [ 11 posts ]


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
cron


Status SimuscapeTerms of UseAbout Simuscape

Design by SAC © 2012-2015, Sweden • Powered by phpBB • Based on twilightBB by Daniel St. Jules