It looks like the setup of the action function and the adding of the states got inverted by something I tried, leaving the ActionFunc member empty on the real states.
- fixed a few problems that were encountered during conversion:
* action specials as action functions were not recognized by the parser.
* Player.StartItem could not be parsed.
* disabled the naming hack for PowerupType. ZScript, unlike DECORATE will never prepend 'Power' to the power's name, it always needs to specified by its full name.
* states and defaults were not checked for empty bodies.
* the scope qualifier for goto labels was not properly converted to a string, because it is an ENamedName, not an FName.
- Every update rolled into one, because I'm pretty sure I missed some while
updating lemon.c (not counting today's commits), since it wasn't always
updated at the same time as lemon.c.
- In particular, I think this check-in from 2016-06-06 was very important to
us after commit 3d5867d29e (For the
Lemon-generated parser, add a new action type SHIFTREDUCE and use it to
further compress the parser tables and improve parser performance.):
* Fix lempar.c so that the shift-reduce optimization works for error
processing.
- started with the AST converter. So far it only deals with direct function calls with simple constants as parameters.
- added an error condition for the defaults block to get rid of some asserts.
- fixed uninitialized counter variable in DECORATE parser.
- allow dottable_id of xxx.color so that the property parser can parse 'powerup.color'.
- fixed crash with actor replacement in script compiler.
- add the lump number to tree nodes because parts of the property parser need that to make decisions.
- removed test stuff.
- converted inventory.txt, player.txt and specialspot.txt to ZSCRIPT. These were the minimal files required to allow actor.txt to parse successfully.
- removed the converted files from the DECORATE include list so that these are entirely handled by ZSCRIPT now.
- split FinishActor into several functions. While DECORATE can, ZSCRIPT cannot do all this in one go.
- split the state finalization into several class-specific virtual functions.
* everything related to scripting is now placed in a subdirectory 'scripting', which itself is separated into DECORATE, ZSCRIPT, the VM and code generation.
* a few items have been moved to different headers so that the DECORATE parser definitions can mostly be kept local. The only exception at the moment is the flags interface on which 3 source files depend.
To do the rest, some cleanup is needed first, to untangle the DECORATE parser from the actual code generation so that the low end stuff can actually be reused here instead of having to be redone.
Ultimately, thingdef should only contain code that is directly related to the DECORATE parser, but that's not the case with this file. It's only function definitions which get used during gameplay and will also be accessed by ZScript.
The change is intentionally on master so that pull requests can adjust to it now instead of creating conflicts later.
* Goto did not support the class scope operator '::'. Like in DECORATE, this cannot be done with a simple '.' because it creates semantic problems with first part of a state label. This requires different syntax so that it can unambiguously distinguish between a scope identifier and the actual label
* Goto used the incorrect token PLUS for '+' instead of ADD.
* The state's duration was not stored in the AST.
* Truncating the sprite name inside the parser is probably not the best idea because it used a simple Printf to report this. Let's do this during processing of the AST where this can be properly handled as an error.
- Sets the absolute amount of an inventory actor.
- Limits itself to the range [0, MaxAmount]. Setting beyondMax to true disregards the MaxAmount. Default is false.
- switched the types of the internal 'self' and 'stateowner' parameters so that they get assigned correctly. I can't tell if this will error out if fields get accessed from the caller with the wrong class, but for actual scripting to work these must be correct.
The committed 'actor.txt' can be parsed successfully, with the exception of a few subclass references that cannot be resolved yet.
This adds:
* builtin types color, state and sound.
* ending a parameter list with an ellipsis to declare a varargs list. (A_Jump uses this.)
* allowing to declare optional arguments by giving them a default value.
* adding an 'action' qualifier for function declarations.
This uses the same property and flag tables as DECORATE with a few changes:
* it sets the parse mode to strict, so that several DECORATE warnings are now errors.
* trying to change a deprecated flag will print a warning.
* setting of editor numbers, spawn and conversation ID id not possible. Use MAPINFO to do this.
* all subclass flags must use the qualified name now (e.g. +ALWAYSPICKUP will print an error.)
* the scriptable Damage property is not yet implemented. This will require a special case with a differently named property in the processing function because in the AST it is no longer possible to distinguish between a damage value and a constant damage function.
This had been defined as a regular compound statement but in the context this will be used in, that makes very little sense, because all it can do is set some constant values.
The most important thing here is that it doesn't provide an unnecessary learning curve to its users and doing it this way will not only ensure that but also avoid redundant documentation.
To allow initialization of other user-defined properties it will require some extensions but that's a job for later and can just as easily be done in the current framework, rather than throwing everything out and start from zero.
- added checks for duplicate field names.
- moved the tree node symbol tables out of PSymbolTreeNode to the worker data. That symbol is a bad location because it restricts the usefulness of the symbol class which is also needed for variables which use different AST structs.
- fixed some memory management issues with the work classes for the compiler that became apparent after moving the symbol tables in there. In several places these were copied around, possibly losing data.
- fixed: The tree nodes for classes and struct members were stored in the global tree nodes table.
- sort variable declarations into their own list for processing.
This uses a different algorithm as the old implementation - instead of recursively resolving unknown symbols it will first collect all constants from all scopes and then process them in one operation, doing multiple passes over the list until no more constants can be resolved anymore.
- fixed class creation. There was an infinite loop and some missing checks for native classes.
- do not write the compiler's symbols to the same symbol table as the output. The output must go to GlobalSymbols but the internal symbols must go to a namespace specific table that can be discarded after compilation.
This is the first thing the compiler has to do to get access to the class's symbol table. Of course at this point the final size of a class is not known yet so these are currently all treated as tentative.
- place generated symbols into GlobalSymbols instead of a scratch table that will be discarded right away.
- allow the state object to change source file scanners (I hope this works, but the old implementation was unable to do more than one with with a parse state so I had to change it.)
- It can now parse constants.txt and insert everything in it into the global symbol table and make subsequent DECORATE compile properly.
Instead of replacing the original, the second class will get renamed now, using the originating file as an identifier. In the vast majority of cases this should do exactly what is needed: Create an unconflicting second class that can coexist with the original. Unless the class is used by name this should eliminate all problems with this, but so far I haven't seen anything that used them by name.
This is choosing the lesser of two evils. While some mod out there may get broken, the old setup meant that the first class of a given name could not be written out to a savegame because it was not retrievable when loading it back.
Ultimately we may have to get a fully qualified name out of this, so Outer should be a type that can handle this feature. The new class for this is currently used as base for PType and PSymbol so that PNamedType inherits from it and maybe later a namespace symbol can, too.
There have been reports about crashes in here with Linux that point to some of the code that gets called here doing unwanted things on the owner, so with these links cleared that should no longer be possible.
- This was fine with fixed point numbers, since they could never be
outside of short range when converted to regular ints. With floating
point numbers now, that condition no longer holds.
- allow recursive linking of $random definitions (as long as they do not link back, see above.)
- fixed the sound precaching which did not handle $alias inside $random. Normally this went undetected but in cases where the random sound index was the same as a sound index in the current link chain this could hang the function.
The slot had been there forever to address this same problem but only one of the two constructors actually set it, too bad that it was the wrong one...
This is something that normally won't be noticed. But if some actor is spawned on a moving platform, with both thinkers on the same statnum it means that the order of execution is not correct with the platform being done first, resulting in the actor to 'jump' while the platform is moving. To prevent this it is necessary that all sector movers only tick after all actors have completed their thinking turn.
We have to be extremely careful with the player data, because there's just too much code littered around that has certain expectations about what needs to be present and what not.
Obviously, when travelling in a hub, the player_t should be retained from the previous level. But we still have to set player_t::mo to the PlayerPawn from the savegame so that G_UnsnapshotLevel doesn't prematurely delete it and all associated voodoo dolls, because it checks player_t::mo to decide whether a player is valid or not.
The actual deletion of this redundant PlayerPawn should only be done in G_FinishTravel, after the actual player has been fully set up
* do not skip the player_t init when travelling in a hub. The old player may still be needed in some edge cases. This applies only to singleplayer for now. The multiplayer version still needs reviewing. I left it alone because it may shuffle players around which is not wanted when doing hub travelling.
* do not spawn two temp players in G_FinishTravel. Instead handle the case where no player_t::mo can be found gracefully by adding a few nullptr checks. This temp player served no real purpose except for having a valid pointer. The actual start position was retrieved from somewhere else.
- Fixed properties not having the proper indices.
- Use ViewPos-to-actor instead of measuring actor-to-actor.
- Use the actual camera instead of the actor so camera textures can work.
I wish I had realized this the last time it came up - it would have saved me a lot of trouble.
But as it turns out, the more recent travelling code makes all of this completely unnecessary, working perfectly fine with deleting the player pawns along with the rest of the thinkers before loading the stored ones from the savegame (and getting rid of those in G_FinishTravel.)
And with a sane savegame format that does not depend on side effects from how the thinker serializing handled linking into the lists the old code was even harmful, leaving voodoo dolls behind.
I had the exact same effect when I tried to reshuffle some things for reliably restoring portals, but did not make the connection to interference between two mutually incompatible player travelling mechanisms that just worked by sheer happenstance with the original order of things.
- changed S_GetMusic to return a const pointer to the actual music name instead of a copy. The only thing this is used for is the savegame code and it has no use for a copy, it can work far more efficiently with a const pointer.
After testing with a savegame on ZDCMP2 which is probably the largest map in existence, timing both methods resulted in a speed difference of less than 40 ms (70 vs 110 ms for reading all sectory, linedefs, sidedefs and objects).
This compares to an overall restoration time, including reloading the level, precaching all textures and setting everything up, of approx. 1.2 s, meaning an increase of 3% of the entire reloading time.
That's simply not worth all the negative side effects that may happen with a method that highly depends on proper code construction.
On the other hand, using random access means that a savegame version change is only needed now when the semantics of a field change, but not if some get added or deleted.
- do not I_Error out in the serializer unless caused by a programming error.
It is better to let the serializer finish, collect all the errors and I_Error out when the game is known to be in a stable enough state to allow unwinding.
It turned out this may not be done automatically when opening the savegame - it has to be done later, after the pre-spawned map thinkers and all connected objects have been destroyed.
The object deserializer also has to be rather careful about dealing with parse errors, because if something goes wrong a whole batch of uninitialized or partially initialized objects will be left behind to destroy.
This means that no object class may assume that anything but the default constructor has been run on it and needs to check any variable it may reference.
- always make the top level object randomaccess when opening a JSON file for reading. Some things won't work right if this is opened for sequential access.
- Several mods were able to just take advantage of A_SetRipperLevel and the likes, essentially bypassing this gate so there really is no point in doing this anymore.
This is so that PNGs can be written to memory, not just to an external file. stdio's FILE cannot be easily redirected but a C++ class can.
The writer is very simple and primitive right now, allowing no seeking, but for the job at hand it is sufficient.
Note that large parts of savegame creation have been disabled, because they are about to be rewritten and it makes no sense to adjust them all before.
- converted sound and canvas texture serialization.
- refactored file_zip, so that it can be used to load loose zip files and extract their compressed data directly.
- added handling to FSerializer to generate and consume compressed Zip file entries.
If all goes well this will allow saving savegames as Zips when the rework is done, which will make analyzing them a lot easier.