Skip to content

Commit

Permalink
Add vectors/dictionaries in event scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
Tirlititi committed Sep 23, 2023
1 parent acf64fd commit 91e94a6
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 106 deletions.
Original file line number Diff line number Diff line change
@@ -1,67 +1,79 @@
using System;
using System.Collections.Generic;
using Memoria.Prime;

namespace Assets.Sources.Scripts.EventEngine.Utils
{
public class CalcStack
{
public Boolean push(Int32 arg0)
public Boolean push(Int32 val)
{
if (this.topOfStackID >= this.stack.Length - 1)
return false;
this.stack[this.topOfStackID] = arg0;
this.topOfStackID++;
while (topOfStackID >= stack.Count)
stack.Add(0);
stack[topOfStackID++] = val;
return true;
}

public Boolean pop(out Int32 output)
{
if (this.topOfStackID == 0)
if (topOfStackID == 0)
{
Log.Error($"[{nameof(CalcStack)}.{nameof(pop)}] this.topOfStackID == 0");
Log.Error($"[{nameof(CalcStack)}.{nameof(pop)}] topOfStackID == 0");
output = default;
return false;
}
output = this.stack[this.topOfStackID - 1];
this.topOfStackID--;
output = stack[--topOfStackID];
return true;
}

public Boolean advanceTopOfStack()
{
if (this.topOfStackID >= this.stack.Length - 1)
return false;
this.topOfStackID++;
topOfStackID++;
while (topOfStackID > stack.Count)
stack.Add(0);
return true;
}

public Boolean retreatTopOfStack()
{
if (this.topOfStackID == 0)
if (topOfStackID == 0)
return false;
this.topOfStackID--;
topOfStackID--;
return true;
}

public void emptyCalcStack()
{
for (Int32 i = 0; i < this.stack.Length; i++)
this.stack[i] = 0;
this.topOfStackID = 0;
substack.Clear();
for (Int32 i = 0; i < stack.Count; i++)
stack[i] = 0;
topOfStackID = 0;
}

public Int32 getTopOfStackID()
{
return this.topOfStackID;
return topOfStackID;
}

public Int32 getValueAtOffset(Int32 offset)
{
return this.stack[this.topOfStackID + offset];
return stack[topOfStackID + offset];
}

public void pushSubs(params Int32[] val)
{
substack[topOfStackID] = new List<Int32>(val);
}

public List<Int32> getSubs()
{
if (substack.TryGetValue(topOfStackID, out List<Int32> result))
return result;
return new List<Int32>();
}

private const Int32 STACK_SIZE = 16;
private Int32[] stack = new Int32[STACK_SIZE];
private Dictionary<Int32, List<Int32>> substack = new Dictionary<Int32, List<Int32>>();
private List<Int32> stack = new List<Int32>(16);
private Int32 topOfStackID;
}
}

1 comment on commit 91e94a6

@Tirlititi
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is now possible to use some kind of arrays and associative arrays in event scripts. These are the equivalent of vector<int> in C++ (or List<int> in C#) and map<int, int> in C++ (or dictionary<int, int> in C#).
It also eases the use of General variables in event scripts, as you don't need to investigate to know which variable is used by the base game's script and which variable isn't: the new variables is a brand new pool of variables that are all unused by default.

In Hades Workshop, you can now use codes like this:

// Initialise an vector with the IDs of all the ultimate weapons
set MemoriaVector(0, 0) = 15 // Ultima Weapon
set MemoriaVector(0, 1) = 30 // Excalibur II
set MemoriaVector(0, 2) = 40 // Dragon's Hair
set MemoriaVector(0, 3) = 50 // Rune Claws
set MemoriaVector(0, 4) = 56 // Tiger Racket
set MemoriaVector(0, 5) = 78 // Mace of Zeus
set MemoriaVector(0, 6) = 84 // Gastro Fork
// Also add Zidane's current weapon in this list
set MemoriaVector(0, 7) = GetCharacterEquipment(0, 0)
// Give a random weapon from that list
AddItem( MemoriaVector(0, GetRandom % MemoriaVectorSize(0)), 1)

The same can be done with MemoriaDictionary in place of MemoriaVector except that MemoriaDictionary can use any index, not only those ranging in [0, MemoriaVectorSize - 1]. They don't have a size. On the other hand, if you try to modify an entry of a MemoriaVector that is not in the range of [0, MemoriaVectorSize], it will have no effect.
For example:

// Clear the 0-th vector
ClearMemoriaVector(0)
set MemoriaVector(0, 10) = 50 // This doesn't work because the 0-th vector has size 0
// Trying to use "MemoriaVector(0, 10)" there will return 0 instead of 50

ClearMemoriaDictionary(0)
set MemoriaDictionary(0, 10) = 50 // This works because you don't need to init the range 0-9 before setting the tenth element for a dictionary
// Using "MemoriaDictionary(0, 10)" there will return 50

The examples above were all written using the 0-th vector and the 0-th dictionary (the first argument in all of these functions) but it would have worked the same with any other. Vectors and dictionaries are also separate pools.
Other than that, you can consider these vector and dictionary entries to be similar to the VAR_GenInt24_XXX variables: they are permanent through the game and are saved in game saves. They can thus be easily used for long-term quests or events.

I would suggest to use MemoriaVector( -1, ... ) and other negative IDs for temporary variables that are not meant to persist through multiple fields, but that's up to you.

The fact that the syntax is MemoriaVector( 0, N ) instead of a more standardised MyFirstVector[N] is because it was simpler for me to implement these variables using the syntax of the functions and because the [N] syntax is already used for accessing the different states of the battle units (although it could have been context-dependant without too much trouble). Also, I'm not a programming language designer ^^'

Please sign in to comment.