| Mar 4, 2026, 12:57 PM | |
|
Custom objects in MLLE, how to design
This is an idea Faw and I were chatting about some years back, and I never got around to implementing it in MLLE. Before I do any coding, though, it'd be good to make sure this is a workable design, so please sing out if you see any potential problems.
Currently MLLE has a "Configure Weapons" window hidden under the "JJ2+ Properties > Weapons" dropdown menu. If you change any weapons in the level (e.g. to one of these), MLLE takes the following steps (also described here) to make that happen:
Code:
Name = Duo Bouncers ImageFilename = duo bouncers.gif LibraryFilename = DuoBouncers.asc Initialization = DuoBouncers::Weapon() Options = Reverse behaviors:bool | Bounce through walls:bool:true The weapon's constructor passes a function (well, a delegate) DetermineReversedness to WeaponInterface::WeaponInterface's apply argument, which looks like this, where parameter corresponds to those two bytes in Data5: Code:
bool DetermineReversedness(uint, se::WeaponHook@, jjSTREAM@ parameter) {
if (parameter !is null && parameter.getSize() >= 2) {
parameter.pop(ReverseBehaviors);
parameter.pop(BounceThroughWalls);
}
return true;
}
For custom objects, instead of weapons, I think a similar system could work, but with a much simpler coding interface, because a) objects are far more variable than weapons and b) objects tend to only need a single jjObjectPresets entry, whereas a weapon needs to configure the bullet, the powered-up bullet, the pickup, the powerup monitor, and the +15 crate. For an interface, I think this should suffice: Code:
shared interface MLLEObject { bool Apply(uint8 eventID, jjSTREAM@ arguments = null); }
The library code would look something like this: Code:
namespace SlowFeet {
array
Code:
Name = Slow Feet LibraryFilename = SlowFeet.asc Initialization = SlowFeet::Pickup() Options = Ticks:uint:70 Hooks = onPlayer:SlowFeet::onPlayer Event = Slow Feet|+|Goodies|Slow|Feet Code:
#include "MLLE-Include-1.9o.asc" #include "SlowFeet.asc" const bool MLLESetupSuccessful = MLLE::Setup(array On the MLLE side of things, there'd be some window for assigning custom objects to event IDs, which would have room for defining object parameters, here the "Ticks" option. Then the "Event" line in the .ini would be read by MLLE and used in place of that event ID's normal event definition, so for example if you wanted to use event 100 for Slow Feet pickups, MLLE would use 100=Slow Feet|+|Goodies|Slow|Feet instead of 100=Tuf Turtle|-|Enemy|Tuf|Turt like normal. Should that window have limitations? Should events 1-32 not be allowed, or just show a warning if you try? What about when the script (or an included library) already overwrites the ini event for a given event ID? Like ///@Event 164=Beams|-|Trigger|Beams? Should you be allowed to try to overwrite the same event ID in both ways? I can think of objects that are more complicated... for example, the poison/antidote system from CloneV would need two jjObjectPresets entries, not just one. But that could be accomplished using the jjSTREAM argument to pass a second uint8. Other times you might want to have multiple instances of the same class, for different event IDs, and then they might have to rely more on delegates than the above example code. But that's all stuff that the library writer would need to handle. Another design decision is how to handle multiple objects that rely on the same standard hook function. Remember that if multiple weapons need onPlayer, you still only see MLLE::WeaponHook.processPlayer(play);, and that one method handles all the different weapons. But under the above model, for multiple objects, you'd see something like: Code:
void onPlayer(jjPLAYER@ play) {
SlowFeet::onPlayer(play);
Poison::sometimesInjurePlayer(play);
RecolorableSprings::checkYSpeed(play);
MLLE::WeaponHook.processPlayer(play);
}
Code:
interface MLLEObject { bool Apply(uint8, jjSTREAM@); }
interface MLLEObject_onPlayer { void onPlayer(jjPLAYER@) }
interface MLLEObject_onMain { void onMain() }
Then MLLE-Include-1.9o.asc would do something like this in the Setup function: Code:
uint8 eventID; jjSTREAM arguments; data5.pop(eventID); data5.pop(arguments); MLLEObject@ instance = initializerArray[0]; instance.Apply(eventID, arguments); MLLEObject_onPlayer@ instanceOnPlayer = cast<MLLEObject_onPlayer@>(instance); if (instanceOnPlayer !is null) someArrayOfOnPlayerFunctions.insertLast(jjVOIDFUNCPLAYER(instanceOnPlayer.onPlayer)); Code:
void ProcessPlayer(jjPLAYER@ player) {
for (uint i = 0; i < SomeArrayOfOnPlayerFunctions.length; ++i)
SomeArrayOfOnPlayerFunctions[i](player);
}
Code:
void onPlayer(jjPLAYER@ play) {
MLLE::ProcessPlayer(play);
}
Code:
namespace SlowFeet {
array
On a related note, one idea Faw suggested was not using .asc files for custom objects at all, but embedding all their code in the j2as, so the scriptwriter could easily make any level-specific changes they wanted. But again, that introduces a lot of room for the scriptwriter to make mistakes... (I know I could use the issues tracker, but the JCF should get more eyeballs.) Last edited by Violet CLM; Mar 4, 2026 at 01:18 PM. |
![]() |
«
Previous Thread
|
Next Thread
»
| Thread Tools | |
|
|
All times are GMT -8. The time now is 02:25 AM.
Jazz2Online © 1999-INFINITY (Site Credits). Jazz Jackrabbit, Jazz Jackrabbit 2, Jazz Jackrabbit Advance and all related trademarks and media are ™ and © Epic Games. Lori Jackrabbit is © Dean Dodrill. J2O development powered by Loops of Fury and Chemical Beats. Powered by vBulletin® Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Original site design by Ovi Demetrian. DrJones is the puppet master. Eat your lima beans, Johnny.



