mirror of
https://github.com/isledecomp/isle-portable.git
synced 2026-05-01 18:13:57 +00:00
Add rabbits extension with two animated rabbits on the mountain top (#791)
Add SiLoader EnableWith directive and HandleEnable hook to trigger loading when a world becomes active. Build rabbits.si from pre-built model (.mod) and animation (.ani) assets using the same ReplaceWithFile pattern as other asset generators. Two rabbits (Blaubart and Fluse) hop between plants with synchronized paths, ear twitching, and tail wagging.
This commit is contained in:
parent
8215544b02
commit
375c496791
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -11,3 +11,4 @@
|
||||
**/*.desktop text eol=lf
|
||||
assets/widescreen/** filter=lfs diff=lfs merge=lfs -text
|
||||
assets/hdmusic/** filter=lfs diff=lfs merge=lfs -text
|
||||
assets/rabbits/** filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
@ -751,6 +751,8 @@ void LegoWorld::Enable(MxBool p_enable)
|
||||
AnimationManager()->Resume();
|
||||
}
|
||||
|
||||
Extension<Extensions::SiLoaderExt>::Call(Extensions::SI::HandleEnable, this, TRUE);
|
||||
|
||||
GameState()->ResetROI();
|
||||
#ifndef BETA10
|
||||
SetIsWorldActive(TRUE);
|
||||
@ -760,6 +762,8 @@ void LegoWorld::Enable(MxBool p_enable)
|
||||
Extension<MultiplayerExt>::Call(MP::HandleWorldEnable, this, TRUE);
|
||||
}
|
||||
else if (!p_enable && m_disabledObjects.size() == 0) {
|
||||
Extension<Extensions::SiLoaderExt>::Call(Extensions::SI::HandleEnable, this, FALSE);
|
||||
|
||||
MxPresenter* presenter;
|
||||
LegoPathController* controller;
|
||||
LegoPathActor* actor = UserActor();
|
||||
|
||||
117
assets/main.cpp
117
assets/main.cpp
@ -6,6 +6,22 @@
|
||||
#include <interleaf.h>
|
||||
#include <object.h>
|
||||
|
||||
void ZeroInitObject(si::Object* o)
|
||||
{
|
||||
o->unknown1_ = 0;
|
||||
o->flags_ = 0;
|
||||
o->unknown4_ = 0;
|
||||
o->duration_ = 0;
|
||||
o->loops_ = 0;
|
||||
o->unknown26_ = 0;
|
||||
o->unknown27_ = 0;
|
||||
o->unknown28_ = 0;
|
||||
o->unknown29_ = 0;
|
||||
o->unknown30_ = 0;
|
||||
o->volume_ = 0;
|
||||
o->time_offset_ = 0;
|
||||
}
|
||||
|
||||
si::Interleaf::Version version = si::Interleaf::Version2_2;
|
||||
uint32_t bufferSize = 65536;
|
||||
uint32_t bufferCount = 8;
|
||||
@ -293,6 +309,106 @@ void CreateBadEnd()
|
||||
si.Write(result.c_str());
|
||||
}
|
||||
|
||||
void CreateRabbits()
|
||||
{
|
||||
std::string result = out + "/rabbits.si";
|
||||
|
||||
si::Interleaf si;
|
||||
mxHd.seek(0, si::MemoryBuffer::SeekStart);
|
||||
si.Read(&mxHd);
|
||||
|
||||
// Object 0 (id=0): Blaubart model (fire-and-forget, loaded by chained Prepend)
|
||||
si::Object* blaubart = new si::Object;
|
||||
ZeroInitObject(blaubart);
|
||||
std::string file = std::string("rabbits/blaubart.mod");
|
||||
std::string extra = "Prepend:/lego/extra/rabbits;1";
|
||||
blaubart->id_ = 0;
|
||||
blaubart->type_ = si::MxOb::Object;
|
||||
blaubart->presenter_ = "LegoModelPresenter";
|
||||
blaubart->name_ = "blaubart";
|
||||
blaubart->flags_ = MxDSAction::c_enabled;
|
||||
blaubart->filetype_ = si::MxOb::OBJ;
|
||||
blaubart->loops_ = 1;
|
||||
blaubart->unknown28_ = 1;
|
||||
blaubart->unknown29_ = 1;
|
||||
blaubart->location_ = si::Vector3(-65.5f, 14.0f, 23.5f);
|
||||
blaubart->direction_ = si::Vector3(0, 0, 1);
|
||||
blaubart->up_ = si::Vector3(0, 1, 0);
|
||||
blaubart->extra_ = si::bytearray(extra.c_str(), extra.length() + 1);
|
||||
if (!blaubart->ReplaceWithFile(file.c_str())) {
|
||||
abort();
|
||||
}
|
||||
|
||||
si.AppendChild(blaubart);
|
||||
depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl;
|
||||
|
||||
// Object 1 (id=1): Fluse model (fire-and-forget, loaded by chained Prepend from Blaubart)
|
||||
si::Object* fluse = new si::Object;
|
||||
ZeroInitObject(fluse);
|
||||
file = std::string("rabbits/fluse.mod");
|
||||
fluse->id_ = 1;
|
||||
fluse->type_ = si::MxOb::Object;
|
||||
fluse->presenter_ = "LegoModelPresenter";
|
||||
fluse->name_ = "fluse";
|
||||
fluse->flags_ = MxDSAction::c_enabled;
|
||||
fluse->filetype_ = si::MxOb::OBJ;
|
||||
fluse->loops_ = 1;
|
||||
fluse->unknown28_ = 1;
|
||||
fluse->unknown29_ = 1;
|
||||
fluse->location_ = si::Vector3(-62.0f, 14.0f, 28.0f);
|
||||
fluse->direction_ = si::Vector3(0, 0, 1);
|
||||
fluse->up_ = si::Vector3(0, 1, 0);
|
||||
if (!fluse->ReplaceWithFile(file.c_str())) {
|
||||
abort();
|
||||
}
|
||||
|
||||
si.AppendChild(fluse);
|
||||
depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl;
|
||||
|
||||
// Object 2 (id=2): LegoAnimMMPresenter (composite)
|
||||
// EnableWith triggers when Isle world becomes active
|
||||
// Prepend chains: loads Blaubart model (id=0), which chains to Fluse model (id=1)
|
||||
si::Object* composite = new si::Object;
|
||||
ZeroInitObject(composite);
|
||||
extra = "EnableWith:\\Lego\\Scripts\\Isle\\Isle;0, Prepend:/lego/extra/rabbits;0";
|
||||
composite->id_ = 2;
|
||||
composite->type_ = si::MxOb::Presenter;
|
||||
composite->presenter_ = "LegoAnimMMPresenter";
|
||||
composite->flags_ = MxDSAction::c_enabled | MxDSAction::c_bit3;
|
||||
composite->loops_ = 1;
|
||||
composite->extra_ = si::bytearray(extra.c_str(), extra.length() + 1);
|
||||
composite->name_ = "rabbits_composite";
|
||||
composite->direction_ = si::Vector3(0, 0, 1);
|
||||
composite->up_ = si::Vector3(0, 1, 0);
|
||||
si.AppendChild(composite);
|
||||
|
||||
// Child (id=3): Combined animation (both rabbits in one anim tree)
|
||||
si::Object* anim = new si::Object;
|
||||
ZeroInitObject(anim);
|
||||
file = std::string("rabbits/rabbits.ani");
|
||||
anim->id_ = 3;
|
||||
anim->type_ = si::MxOb::Object;
|
||||
anim->presenter_ = "LegoLoopingAnimPresenter";
|
||||
anim->name_ = "rabbits_anim";
|
||||
anim->flags_ = MxDSAction::c_enabled | si::MxOb::NoLoop;
|
||||
anim->filetype_ = si::MxOb::OBJ;
|
||||
anim->duration_ = -1;
|
||||
anim->loops_ = 1;
|
||||
anim->unknown28_ = 1;
|
||||
anim->unknown29_ = 1;
|
||||
anim->location_ = si::Vector3(0, 0, 0);
|
||||
anim->direction_ = si::Vector3(0, 0, 1);
|
||||
anim->up_ = si::Vector3(0, 1, 0);
|
||||
if (!anim->ReplaceWithFile(file.c_str())) {
|
||||
abort();
|
||||
}
|
||||
|
||||
composite->AppendChild(anim);
|
||||
depfile << result << ": " << (std::filesystem::current_path() / file).string() << std::endl;
|
||||
|
||||
si.Write(result.c_str());
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
out = argv[1];
|
||||
@ -307,5 +423,6 @@ int main(int argc, char* argv[])
|
||||
CreateWidescreen();
|
||||
CreateHDMusic();
|
||||
CreateBadEnd();
|
||||
CreateRabbits();
|
||||
return 0;
|
||||
}
|
||||
|
||||
3
assets/rabbits/blaubart.mod
Normal file
3
assets/rabbits/blaubart.mod
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ac169fa2b91c3cda2748a1164a05d4d9a4e1daabe01377512a509e2f406925b0
|
||||
size 1686652
|
||||
3
assets/rabbits/fluse.mod
Normal file
3
assets/rabbits/fluse.mod
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cb4093239917c305075a8ed952b264cbfa504210c5a67da616756342f57f7f36
|
||||
size 1687240
|
||||
3
assets/rabbits/rabbits.ani
Normal file
3
assets/rabbits/rabbits.ani
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5687e8f5b0cd15d19740b0b04724b5bf994e1a0e9659f556429c268fea280ebd
|
||||
size 16334
|
||||
@ -27,6 +27,7 @@ class SiLoaderExt {
|
||||
static std::optional<MxBool> HandleRemove(StreamObject p_object, LegoWorld* p_world);
|
||||
static std::optional<MxBool> HandleDelete(MxDSAction& p_action);
|
||||
static MxBool HandleEndAction(MxEndActionNotificationParam& p_param);
|
||||
static MxBool HandleEnable(LegoWorld* p_world, MxBool p_enable);
|
||||
|
||||
template <typename... Args>
|
||||
static std::optional<StreamObject> ReplacedIn(MxDSAction& p_action, Args... p_args);
|
||||
@ -41,10 +42,12 @@ class SiLoaderExt {
|
||||
static std::vector<std::string> directives;
|
||||
static std::vector<std::pair<StreamObject, StreamObject>> startWith;
|
||||
static std::vector<std::pair<StreamObject, StreamObject>> removeWith;
|
||||
static std::vector<std::pair<StreamObject, StreamObject>> enableWith;
|
||||
static std::vector<std::pair<StreamObject, StreamObject>> replace;
|
||||
static std::vector<std::pair<StreamObject, StreamObject>> prepend;
|
||||
static std::vector<StreamObject> fullScreenMovie;
|
||||
static std::vector<StreamObject> disable3d;
|
||||
static std::vector<StreamObject> firedPrepend;
|
||||
|
||||
static bool LoadFile(const char* p_file);
|
||||
static bool LoadDirective(const char* p_directive);
|
||||
@ -84,6 +87,7 @@ constexpr auto HandleWorld = &SiLoaderExt::HandleWorld;
|
||||
constexpr auto HandleRemove = &SiLoaderExt::HandleRemove;
|
||||
constexpr auto HandleDelete = &SiLoaderExt::HandleDelete;
|
||||
constexpr auto HandleEndAction = &SiLoaderExt::HandleEndAction;
|
||||
constexpr auto HandleEnable = &SiLoaderExt::HandleEnable;
|
||||
constexpr auto ReplacedIn = [](auto&&... args) {
|
||||
return SiLoaderExt::ReplacedIn(std::forward<decltype(args)>(args)...);
|
||||
};
|
||||
@ -95,6 +99,7 @@ constexpr decltype(&SiLoaderExt::HandleWorld) HandleWorld = nullptr;
|
||||
constexpr decltype(&SiLoaderExt::HandleRemove) HandleRemove = nullptr;
|
||||
constexpr decltype(&SiLoaderExt::HandleDelete) HandleDelete = nullptr;
|
||||
constexpr decltype(&SiLoaderExt::HandleEndAction) HandleEndAction = nullptr;
|
||||
constexpr decltype(&SiLoaderExt::HandleEnable) HandleEnable = nullptr;
|
||||
constexpr auto ReplacedIn = [](auto&&... args) -> std::optional<SiLoaderExt::StreamObject> {
|
||||
((void) args, ...);
|
||||
return std::nullopt;
|
||||
|
||||
@ -19,10 +19,12 @@ std::vector<std::string> SiLoaderExt::files;
|
||||
std::vector<std::string> SiLoaderExt::directives;
|
||||
std::vector<std::pair<SiLoaderExt::StreamObject, SiLoaderExt::StreamObject>> SiLoaderExt::startWith;
|
||||
std::vector<std::pair<SiLoaderExt::StreamObject, SiLoaderExt::StreamObject>> SiLoaderExt::removeWith;
|
||||
std::vector<std::pair<SiLoaderExt::StreamObject, SiLoaderExt::StreamObject>> SiLoaderExt::enableWith;
|
||||
std::vector<std::pair<SiLoaderExt::StreamObject, SiLoaderExt::StreamObject>> SiLoaderExt::replace;
|
||||
std::vector<std::pair<SiLoaderExt::StreamObject, SiLoaderExt::StreamObject>> SiLoaderExt::prepend;
|
||||
std::vector<SiLoaderExt::StreamObject> SiLoaderExt::fullScreenMovie;
|
||||
std::vector<SiLoaderExt::StreamObject> SiLoaderExt::disable3d;
|
||||
std::vector<SiLoaderExt::StreamObject> SiLoaderExt::firedPrepend;
|
||||
bool SiLoaderExt::enabled = false;
|
||||
|
||||
void SiLoaderExt::Initialize()
|
||||
@ -108,6 +110,10 @@ std::optional<MxResult> SiLoaderExt::HandleStart(MxDSAction& p_action)
|
||||
if (p_action.GetExtraLength() == 0 || !SDL_strstr(p_action.GetExtraData(), prependedMarker)) {
|
||||
for (const auto& key : prepend) {
|
||||
if (key.first == object) {
|
||||
auto it = std::find(firedPrepend.begin(), firedPrepend.end(), key.second);
|
||||
if (it != firedPrepend.end()) {
|
||||
firedPrepend.erase(it);
|
||||
}
|
||||
MxDSAction action;
|
||||
MxResult result = start(key.second, p_action, action);
|
||||
|
||||
@ -155,6 +161,46 @@ MxBool SiLoaderExt::HandleWorld(LegoWorld* p_world)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
MxBool SiLoaderExt::HandleEnable(LegoWorld* p_world, MxBool p_enable)
|
||||
{
|
||||
StreamObject object{p_world->GetAtomId(), p_world->GetEntityId()};
|
||||
|
||||
if (p_enable) {
|
||||
firedPrepend.clear();
|
||||
|
||||
auto start = [](const StreamObject& p_object, MxDSAction& p_out) {
|
||||
if (!OpenStream(p_object.first.GetInternal())) {
|
||||
return;
|
||||
}
|
||||
|
||||
p_out.SetAtomId(p_object.first);
|
||||
p_out.SetObjectId(p_object.second);
|
||||
p_out.SetUnknown24(-1);
|
||||
Start(&p_out);
|
||||
};
|
||||
|
||||
for (const auto& key : enableWith) {
|
||||
if (key.first == object) {
|
||||
MxDSAction action;
|
||||
start(key.second, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const auto& key : enableWith) {
|
||||
if (key.first == object) {
|
||||
MxDSAction action;
|
||||
action.SetAtomId(key.second.first);
|
||||
action.SetObjectId(key.second.second);
|
||||
action.SetUnknown24(-1);
|
||||
DeleteObject(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
std::optional<MxBool> SiLoaderExt::HandleRemove(StreamObject p_object, LegoWorld* p_world)
|
||||
{
|
||||
for (const auto& key : removeWith) {
|
||||
@ -226,7 +272,9 @@ MxBool SiLoaderExt::HandleEndAction(MxEndActionNotificationParam& p_param)
|
||||
|
||||
if (!p_param.GetSender() || !p_param.GetSender()->IsA("MxCompositePresenter")) {
|
||||
for (const auto& key : prepend) {
|
||||
if (key.second == object) {
|
||||
if (key.second == object &&
|
||||
std::find(firedPrepend.begin(), firedPrepend.end(), object) == firedPrepend.end()) {
|
||||
firedPrepend.push_back(object);
|
||||
MxDSAction action;
|
||||
start(key.first, *p_param.GetAction(), action);
|
||||
}
|
||||
@ -280,6 +328,12 @@ bool SiLoaderExt::LoadDirective(const char* p_directive)
|
||||
StreamObject{MxAtomId{targetAtom, e_lowerCase2}, targetId}
|
||||
);
|
||||
}
|
||||
else if (SDL_sscanf(p_directive, "EnableWith:%255[^:;]%*[:;]%u%*[:;]%255[^:;]%*[:;]%u", originAtom, &originId, targetAtom, &targetId) == 4) {
|
||||
enableWith.emplace_back(
|
||||
StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId},
|
||||
StreamObject{MxAtomId{targetAtom, e_lowerCase2}, targetId}
|
||||
);
|
||||
}
|
||||
else if (SDL_sscanf(p_directive, "Replace:%255[^:;]%*[:;]%u%*[:;]%255[^:;]%*[:;]%u", originAtom, &originId, targetAtom, &targetId) == 4) {
|
||||
replace.emplace_back(
|
||||
StreamObject{MxAtomId{originAtom, e_lowerCase2}, originId},
|
||||
@ -342,6 +396,15 @@ void SiLoaderExt::ParseExtra(const MxAtomId& p_atom, si::Core* p_core)
|
||||
}
|
||||
}
|
||||
|
||||
if ((directive = SDL_strstr(extra.c_str(), "EnableWith:"))) {
|
||||
if (SDL_sscanf(directive, "EnableWith:%255[^:;]%*[:;]%u", atom, &id) == 2) {
|
||||
enableWith.emplace_back(
|
||||
StreamObject{MxAtomId{atom, e_lowerCase2}, id},
|
||||
StreamObject{p_atom, object->id_}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ((directive = SDL_strstr(extra.c_str(), "Replace:"))) {
|
||||
if (SDL_sscanf(directive, "Replace:%255[^:;]%*[:;]%u", atom, &id) == 2) {
|
||||
replace.emplace_back(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user