Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
285 changes: 251 additions & 34 deletions src/data_win.c

Large diffs are not rendered by default.

98 changes: 98 additions & 0 deletions src/data_win.h
Original file line number Diff line number Diff line change
Expand Up @@ -631,11 +631,25 @@ typedef struct {
float rotation;
} SpriteInstance;

typedef struct {
const char* name;
int32_t sequenceIndex;
int32_t x, y;
float scaleX, scaleY;
uint32_t color;
float animationSpeed;
uint32_t animationSpeedType;
float frameIndex;
float rotation;
} SequenceInstance;

typedef struct {
uint32_t legacyTileCount;
RoomTile *legacyTiles;
uint32_t spriteCount;
SpriteInstance *sprites;
uint32_t sequenceCount;
SequenceInstance* sequences;
} RoomLayerAssetsData;

typedef struct {
Expand Down Expand Up @@ -782,6 +796,89 @@ typedef struct {
AnimCurveChannel** allChannels;
} Acrv;

typedef enum {
SEQUENCE_TRACK_GROUP,
SEQUENCE_TRACK_GRAPHIC,
SEQUENCE_TRACK_INSTANCE,
SEQUENCE_TRACK_SEQUENCE,
SEQUENCE_TRACK_AUDIO,
SEQUENCE_TRACK_TEXT,
SEQUENCE_TRACK_REAL,
SEQUENCE_TRACK_COLOR,
SEQUENCE_TRACK_BOOL,
SEQUENCE_TRACK_STRING,
SEQUENCE_TRACK_SPRITE_FRAMES,
SEQUENCE_TRACK_MESSAGE,
SEQUENCE_TRACK_MOMENT
} SequenceTrackType;

typedef struct {
int32_t id;
float real;
int32_t integer;
int32_t curveIndex;
AnimCurve* embeddedCurve;
const char* text;
uint32_t messageCount;
const char** messages;
bool wrap;
int32_t alignment;
int32_t fontIndex;
int32_t audioMode;
} SequenceChannel;

typedef struct {
float key, length;
bool stretch, disabled;
uint32_t channelCount;
SequenceChannel* channels;
} SequenceKeyframe;

typedef struct SequenceTrack {
const char* name;
SequenceTrackType type;
uint32_t builtinName;
uint32_t traits;
bool creationTrack;
bool enabled, visible;
uint32_t trackCount;
struct SequenceTrack* tracks;
uint32_t curveCount;
AnimCurve* curves;
int32_t interpolation;
uint32_t keyframeCount;
SequenceKeyframe* keyframes;
} SequenceTrack;

typedef struct {
int32_t id;
const char* name;
} SequenceFunction;

typedef struct {
bool present;
const char* name;
uint32_t playback;
float playbackSpeed;
uint32_t playbackSpeedType;
float length;
int32_t originX, originY;
float volume;
uint32_t trackCount;
SequenceTrack* tracks;
uint32_t messageCount;
SequenceKeyframe* messages;
uint32_t momentCount;
SequenceKeyframe* moments;
uint32_t functionCount;
SequenceFunction* functions;
} Sequence;

typedef struct {
uint32_t count;
Sequence* sequences;
} Seqn;

// ===[ TPAG - Texture Page Items ]===
typedef struct {
bool present;
Expand Down Expand Up @@ -943,6 +1040,7 @@ struct DataWin {
// DAFL is empty, no field needed
Tgin tgin;
Acrv acrv;
Seqn seqn;
Tpag tpag;
Code code;
Vari vari;
Expand Down
1 change: 1 addition & 0 deletions src/instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct Instance {
// Used to track which alarms are set without looping through the entire alarm array
uint16_t activeAlarmMask;
bool cleanupEventFired;
bool sequenceControlled;
int32_t maskIndex; // collision mask sprite override (-1 = use spriteIndex)
int32_t* collisionCells; // Used to track where we are
uint32_t lastCollisionQueryId;
Expand Down
106 changes: 74 additions & 32 deletions src/runner.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ void Runner_freeRuntimeLayer(RuntimeLayer* runtimeLayer) {
free(el->spriteElement);
el->spriteElement = nullptr;
}
if (el->sequenceElement != nullptr) {
el->sequenceElement->destroyed = true;
Sequence_release(el->sequenceElement);
el->sequenceElement = nullptr;
}
if (el->tileElement != nullptr && el->tileElementOwned) {
free(el->tileElement);
el->tileElement = nullptr;
Expand Down Expand Up @@ -912,7 +917,7 @@ static void rebuildDrawableCacheIfDirty(Runner* runner) {
}
}

static void drawInstanceNormally(Runner* runner, Instance* inst) {
void Runner_drawInstance(Runner* runner, Instance* inst) {
if (runner->drawEnabled) {
int32_t ownerObjectIndex = -1;
int32_t codeId = findEventCodeIdAndOwner(runner, inst->objectIndex, EVENT_DRAW, DRAW_NORMAL, &ownerObjectIndex);
Expand Down Expand Up @@ -1029,10 +1034,10 @@ void Runner_draw(Runner* runner) {
Instance* inst = d->instance;
if (d->layerOrder != 0) continue;
// Filter inactive/invisible instances at draw time so the cache doesn't need invalidation when those flags toggle.
if (!inst->active || !inst->visible) continue;
if (!inst->active || !inst->visible || inst->sequenceControlled) continue;

int32_t previousShader = Runner_pushLayerShader(runner, inst->layer);
drawInstanceNormally(runner, inst);
Runner_drawInstance(runner, inst);
Runner_popLayerShader(runner, previousShader);
} else if (d->type == DRAWABLE_PARTICLE_SYSTEM) {
// Filtered at draw time, like instance visibility: part_system_automatic_draw can be
Expand Down Expand Up @@ -1076,8 +1081,8 @@ void Runner_draw(Runner* runner) {
RuntimeLayerElement* el = &runtimeLayer->elements[j];
if (el->type != RuntimeLayerElementType_Instance) continue;
Instance* inst = hmget(runner->instancesById, el->instanceId);
if (inst == nullptr || !inst->active || !inst->visible || inst->destroyed) continue;
drawInstanceNormally(runner, inst);
if (inst == nullptr || !inst->active || !inst->visible || inst->destroyed || inst->sequenceControlled) continue;
Runner_drawInstance(runner, inst);
}
runtimeLayer = Runner_findRuntimeLayerById(runner, d->runtimeLayerId);
if (runtimeLayer == nullptr) {
Expand All @@ -1091,8 +1096,12 @@ void Runner_draw(Runner* runner) {
float roomH = (float) runner->currentRoom->height;
size_t elementCount = arrlenu(runtimeLayer->elements);
repeat(elementCount, j) {
runtimeLayer = Runner_findRuntimeLayerById(runner, d->runtimeLayerId);
if (runtimeLayer == nullptr || (size_t)j >= arrlenu(runtimeLayer->elements)) break;
RuntimeLayerElement* layerElement = &runtimeLayer->elements[j];
if (layerElement->type == RuntimeLayerElementType_Background && layerElement->backgroundElement != nullptr) {
if (layerElement->type == RuntimeLayerElementType_Sequence) {
if (layerElement->visible) Sequence_draw(layerElement->sequenceElement);
} else if (layerElement->type == RuntimeLayerElementType_Background && layerElement->backgroundElement != nullptr) {
renderBackgroundElement(runner, layerElement->backgroundElement, roomW, roomH, layerOffsetX, layerOffsetY);
} else if (layerElement->type == RuntimeLayerElementType_Tile && layerElement->tileElement != nullptr && layerElement->tileElementOwned) {
if (!layerElement->visible) continue;
Expand Down Expand Up @@ -1769,6 +1778,20 @@ static void initRoom(Runner* runner, int32_t roomIndex) {
if (layerSource->type != RoomLayerType_Assets || layerSource->assetsData == nullptr) continue;
RoomLayerAssetsData* assets = layerSource->assetsData;
RuntimeLayer* runtimeLayer = &runner->runtimeLayers[i];
repeat(assets->sequenceCount, sequenceIndex) {
SequenceInstance* src = &assets->sequences[sequenceIndex];
int32_t id = Sequence_create(runner, (int32_t)layerSource->id, src->sequenceIndex, (float)src->x, (float)src->y);
RuntimeSequence* seq = Sequence_find(runner, id);
if (seq != nullptr) {
seq->scaleX = src->scaleX;
seq->scaleY = src->scaleY;
seq->angle = src->rotation;
seq->headPosition = src->frameIndex;
seq->speedScale = src->animationSpeed;
seq->blend = src->color & 0xFFFFFFu;
seq->alpha = (float)BGR_A(src->color) / 255.0f;
}
}
repeat(assets->spriteCount, j) {
SpriteInstance* src = &assets->sprites[j];
RuntimeSpriteElement* spriteElement = (RuntimeSpriteElement *)safeMalloc(sizeof(RuntimeSpriteElement));
Expand Down Expand Up @@ -2650,6 +2673,13 @@ Runner* Runner_create(DataWin* dataWin, VMContext* vm, Renderer* renderer, FileS
}
}

{
repeat(dataWin->seqn.count, i) {
if (!dataWin->seqn.sequences[i].present) continue;
shput(runner->assetsByName, dataWin->seqn.sequences[i].name, i);
}
}

{
repeat(shlen(vm->builtinMap), i) {
bool isRegistered = shgeti(vm->codeIndexByName, vm->builtinMap[i].key) != -1;
Expand Down Expand Up @@ -4349,34 +4379,41 @@ static void queueSpriteMessages(Runner* runner, PendingSpriteMessage** pending,
queueSpriteMessageRange(pending, sprite, elementId, start, end);
}

void Runner_broadcastMessage(Runner* runner, const char* type, const char* message, int32_t elementId) {
DsMapEntry* map = nullptr;
int32_t mapId = -1;
repeat(arrlenu(runner->dsMapPool), j) {
if (runner->dsMapPool[j] == nullptr) {
mapId = (int32_t)j;
break;
}
}
if (mapId < 0) {
arrput(runner->dsMapPool, map);
mapId = (int32_t)arrlen(runner->dsMapPool) - 1;
}
DsMapEntry** mapPtr = &runner->dsMapPool[mapId];
shput(*mapPtr, safeStrdup("event_type"), RValue_makeOwnedString(safeStrdup(type)));
shput(*mapPtr, safeStrdup("element_id"), RValue_makeReal((GMLReal)elementId));
shput(*mapPtr, safeStrdup("message"), RValue_makeOwnedString(safeStrdup(message)));
int32_t previousMapId = runner->eventDataMapId;
runner->eventDataMapId = mapId;
Runner_executeEventForAll(runner, EVENT_OTHER, OTHER_BROADCAST_MESSAGE);
runner->eventDataMapId = previousMapId;
mapPtr = &runner->dsMapPool[mapId];
if (*mapPtr != nullptr) {
repeat(shlen(*mapPtr), j) {
free((*mapPtr)[j].key);
RValue_free(&(*mapPtr)[j].value);
}
shfree(*mapPtr);
*mapPtr = nullptr;
}
}

static void dispatchSpriteMessages(Runner* runner, PendingSpriteMessage* pending) {
repeat(arrlenu(pending), i) {
DsMapEntry* map = nullptr;
int32_t mapId = -1;
repeat(arrlenu(runner->dsMapPool), j) {
if (runner->dsMapPool[j] == nullptr) { mapId = (int32_t) j; break; }
}
if (mapId < 0) {
arrput(runner->dsMapPool, map);
mapId = (int32_t) arrlen(runner->dsMapPool) - 1;
}
DsMapEntry** mapPtr = &runner->dsMapPool[mapId];
shput(*mapPtr, safeStrdup("event_type"), RValue_makeOwnedString(safeStrdup("sprite event")));
shput(*mapPtr, safeStrdup("element_id"), RValue_makeReal((GMLReal) pending[i].elementId));
shput(*mapPtr, safeStrdup("message"), RValue_makeOwnedString(safeStrdup(pending[i].message)));
int32_t previousMapId = runner->eventDataMapId;
runner->eventDataMapId = mapId;
Runner_executeEventForAll(runner, EVENT_OTHER, OTHER_BROADCAST_MESSAGE);
runner->eventDataMapId = previousMapId;
mapPtr = &runner->dsMapPool[mapId];
if (*mapPtr != nullptr) {
repeat(shlen(*mapPtr), j) {
free((*mapPtr)[j].key);
RValue_free(&(*mapPtr)[j].value);
}
shfree(*mapPtr);
*mapPtr = nullptr;
}
Runner_broadcastMessage(runner, "sprite event", pending[i].message, pending[i].elementId);
}
}

Expand Down Expand Up @@ -4601,6 +4638,8 @@ void Runner_step(Runner* runner) {

dispatchMouseEvents(runner);

Sequence_stepRoom(runner);

// Execute Normal Step for all instances
Runner_executeEventForAll(runner, EVENT_STEP, STEP_NORMAL);

Expand All @@ -4613,6 +4652,7 @@ void Runner_step(Runner* runner) {
repeat(motionCount, mi) {
Instance* inst = runner->instances[mi];
if (!inst->active) continue;
if (inst->sequenceControlled) continue;
if (runner->physics) {
if (inst->pathIndex >= 0 && (!inst->physicsBody || !PhysicsEngine_variable(inst->physicsBody, PHY_DYNAMIC, 0, 0, (float)Runner_getEffectiveGameSpeed(runner)))) {
if (adaptPath(runner, inst)) Runner_executeEvent(runner, inst, EVENT_OTHER, OTHER_END_OF_PATH);
Expand Down Expand Up @@ -5285,6 +5325,8 @@ void Runner_free(Runner* runner) {
arrfree(runner->cachedDrawables);
runner->cachedDrawables = nullptr;
arrfree(runner->instanceSnapshots);
arrfree(runner->sequenceSnapshots);
arrfree(runner->sequenceEvents);
runner->instanceSnapshots = nullptr;
arrfree(runner->eventDispatchInstances);
runner->eventDispatchInstances = nullptr;
Expand Down
7 changes: 7 additions & 0 deletions src/runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "runner_mouse.h"
#include "vm.h"
#include "random.h"
#include "sequence.h"

// ===[ Event Type Constants ]===
#define EVENT_ROOM_CREATION -2
Expand Down Expand Up @@ -231,6 +232,7 @@ typedef enum {
RuntimeLayerElementType_Sprite = 4,
RuntimeLayerElementType_Tilemap = 5,
RuntimeLayerElementType_Tile = 7,
RuntimeLayerElementType_Sequence = 8,
} RuntimeLayerElementType;

typedef struct {
Expand All @@ -241,6 +243,7 @@ typedef struct {
uint32_t blend; // GameMaker-HTML5's m_imageBlend
RuntimeBackgroundElement* backgroundElement; // owned; set for every background element
RuntimeSpriteElement* spriteElement; // owned; nullptr if type != Sprite
RuntimeSequence* sequenceElement;
RoomTile* tileElement; // borrowed (legacyTiles) or owned (layer_tile_create); nullptr if type != Tile
bool tileElementOwned; // true if tileElement was heap-allocated and must be freed
RoomLayerTilesData* tilemapData; // borrowed, points into the parsed RoomLayer; nullptr if type != Tilemap
Expand Down Expand Up @@ -713,6 +716,8 @@ struct Runner {
// Any loop that might fire user code iterates a copy so that in-flight mutations (instance_change swap-remove, spawns, destroys) don't corrupt it.
// Each call pushes its snapshot (append) and pops on normal loop exit; nesting is safe because pushes/pops are LIFO and outer ranges stay untouched under newer pushes.
Instance** instanceSnapshots;
RuntimeSequence** sequenceSnapshots;
SequenceEvent* sequenceEvents;
SpatialGrid* spatialGrid;
uint32_t collisionQueryCounter;
int32_t pendingRoom; // -1 = none
Expand Down Expand Up @@ -939,6 +944,8 @@ void Runner_setLives(Runner* runner, GMLReal value);
// Used to fire events when the values are equal to or lesser than 0.
void Runner_setHealth(Runner* runner, GMLReal value);
void Runner_draw(Runner* runner);
void Runner_drawInstance(Runner* runner, Instance* inst);
void Runner_broadcastMessage(Runner* runner, const char* type, const char* message, int32_t elementId);
// Ensures the application_surface exists at the right size, mirrors the renderer's ID into runner+renderer state, then
// invokes renderer->vtable->beginFrame. Every platform main should call this instead of beginFrame directly.
void Runner_beginFrame(Runner* runner, int32_t gameW, int32_t gameH, int32_t windowW, int32_t windowH, int32_t framebufferW, int32_t framebufferH);
Expand Down
Loading
Loading