diff --git a/src/file/replay/replay.d b/src/file/replay/replay.d index b45dbb58..a8735a0a 100644 --- a/src/file/replay/replay.d +++ b/src/file/replay/replay.d @@ -183,21 +183,52 @@ public: _players[nr] = p; } - bool wasPlayedBy(string who) const pure nothrow @safe @nogc - { - return _players.byValue.canFind!(pl => pl.name == who); - } + const pure nothrow @safe @nogc { + bool wasPlayedBy(string who) + { + return _players.byValue.canFind!(pl => pl.name == who); + } - // This doesn't check whether the metadata/general data is the same. - // We assume that Game only calls this on replays of the same level. - // "Before" is exclusive, you might want to pass Phyu(now + 1). - bool equalBefore(in Replay rhs, in Phyu before) const @nogc nothrow - in { - assert (rhs !is null); + /* + * equalBefore(), extends(): + * These don't check whether the metadata/general data is the same. + * We assume that Game only calls this on replays of the same level. + * "Before" is exclusive, you might want to pass Phyu(now + 1). + */ + bool equalBefore(in Replay rhs, in Phyu t) + in { assert (rhs !is null); } + do { + return this.plySliceBefore(t) == rhs.plySliceBefore(t); + } + + bool extends(in Replay rhs) + in { assert (rhs !is null); } + do { + immutable rlen = rhs._plies.length; + return _plies.length >= rlen && _plies[0 .. rlen] == rhs._plies[]; + } + + /* + * Call allPlies() rarely, e.g., to list all entries in the tweaker. + * Prefer to call plySliceFor(). + */ + const(Ply)[] allPlies() { return _plies; } + const(Ply)[] plySliceFor(in Phyu upd) + { + auto slice = this.plySliceBefore(Phyu(upd + 1)); + int firstGood = slice.len; + while (firstGood > 0 && slice[firstGood - 1].update == upd) + --firstGood; + assert (firstGood >= 0); + assert (firstGood <= slice.length); + return _plies[firstGood .. slice.length]; + } } - do { - return this.plySliceBefore(before) - == rhs.plySliceBefore(before); + + void add(in Ply d) + { + touch(); + this.addWithoutTouching(d); } void eraseEarlySingleplayerNukes() @@ -271,43 +302,6 @@ public: touch(); } - /* - * Our users should prefer to call plySliceFor() over allPlies(). - */ - const(Ply)[] plySliceFor(in Phyu upd) const pure nothrow @nogc - { - auto slice = this.plySliceBefore(Phyu(upd + 1)); - int firstGood = slice.len; - while (firstGood > 0 && slice[firstGood - 1].update == upd) - --firstGood; - assert (firstGood >= 0); - assert (firstGood <= slice.length); - return _plies[firstGood .. slice.length]; - } - - /* - * Call allPlies() rarely, e.g., to list all entries in the replay editor. - */ - @property const(Ply)[] allPlies() const pure nothrow @nogc - { - return _plies; - } - - bool getOnPhyuLixClicked(in Phyu upd, in int lix_id, in Ac ac) const - { - auto vec = plySliceFor(upd); - foreach (const ref d; vec) - if (d.isSomeAssignment && d.toWhichLix == lix_id && d.skill == ac) - return true; - return false; - } - - void add(in Ply d) - { - touch(); - this.addWithoutTouching(d); - } - /* * See file.replay.change for what it returns. */ diff --git a/src/file/replay/tweakimp.d b/src/file/replay/tweakimp.d index c0eaf95a..ba8c3726 100644 --- a/src/file/replay/tweakimp.d +++ b/src/file/replay/tweakimp.d @@ -62,7 +62,7 @@ do { inout(Ply)[] plySliceBefore( inout(Replay) rep, in Phyu upd -) pure nothrow @nogc { with (rep) +) pure nothrow @safe @nogc { with (rep) { // The binary search algo works also for this case. // But we add mostly to the end of the data, so check here for speed. diff --git a/src/game/nurse/savestat.d b/src/game/nurse/savestat.d index 6d8ddc9e..331d81ec 100644 --- a/src/game/nurse/savestat.d +++ b/src/game/nurse/savestat.d @@ -62,9 +62,15 @@ public: auto loaded = _cache.loadUser(replay, Phyu(cs.update + 1)); model.takeOwnershipOf(loaded.state.clone); - if (! replay.equalBefore(loaded.replay, Phyu(upd + 1))) { + // Now, upd is the loaded state's physics update, not our old update. + immutable bool eqb = replay.equalBefore(loaded.replay, Phyu(upd + 1)); + immutable bool ext = replay.extends(loaded.replay); + if (! eqb || ! ext) { replay = loaded.replay.clone(); - onCutGlobalFutureFromReplay(); // don't cut, but maybe play sound + if (! eqb) { + // A visible difference already at upd(). + onCutGlobalFutureFromReplay(); // Don't cut, but play sound. + } } if (! replayAfterFrameBack.value) { cutGlobalFutureFromReplay();