mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-26 13:09:56 +03:00
Updating the dialogue module.
This commit is contained in:
parent
ad2c1d5d2c
commit
fa489d603c
2 changed files with 333 additions and 13 deletions
|
@ -418,7 +418,7 @@ struct Sound {
|
|||
|
||||
/// Checks if the sound is not loaded.
|
||||
bool isEmpty() {
|
||||
if (data.isKind!(rl.Sound)) {
|
||||
if (data.isType!(rl.Sound)) {
|
||||
return data.get!(rl.Sound)().stream.sampleRate == 0;
|
||||
} else {
|
||||
return data.get!(rl.Music)().stream.sampleRate == 0;
|
||||
|
@ -428,7 +428,7 @@ struct Sound {
|
|||
/// Returns true if the sound is playing.
|
||||
@trusted
|
||||
bool isPlaying() {
|
||||
if (data.isKind!(rl.Sound)) {
|
||||
if (data.isType!(rl.Sound)) {
|
||||
return rl.IsSoundPlaying(data.get!(rl.Sound)());
|
||||
} else {
|
||||
return rl.IsMusicStreamPlaying(data.get!(rl.Music)());
|
||||
|
@ -438,7 +438,7 @@ struct Sound {
|
|||
/// Returns the current playback time of the sound.
|
||||
@trusted
|
||||
float time() {
|
||||
if (data.isKind!(rl.Sound)) {
|
||||
if (data.isType!(rl.Sound)) {
|
||||
return 0.0f;
|
||||
} else {
|
||||
return rl.GetMusicTimePlayed(data.get!(rl.Music)());
|
||||
|
@ -448,7 +448,7 @@ struct Sound {
|
|||
/// Returns the total duration of the sound.
|
||||
@trusted
|
||||
float duration() {
|
||||
if (data.isKind!(rl.Sound)) {
|
||||
if (data.isType!(rl.Sound)) {
|
||||
return 0.0f;
|
||||
} else {
|
||||
return rl.GetMusicTimeLength(data.get!(rl.Music)());
|
||||
|
@ -464,7 +464,7 @@ struct Sound {
|
|||
/// Sets the volume level for the sound.
|
||||
@trusted
|
||||
void setVolume(float value) {
|
||||
if (data.isKind!(rl.Sound)) {
|
||||
if (data.isType!(rl.Sound)) {
|
||||
rl.SetSoundVolume(data.get!(rl.Sound)(), value);
|
||||
} else {
|
||||
rl.SetMusicVolume(data.get!(rl.Music)(), value);
|
||||
|
@ -474,7 +474,7 @@ struct Sound {
|
|||
/// Sets the pitch of the sound.
|
||||
@trusted
|
||||
void setPitch(float value) {
|
||||
if (data.isKind!(rl.Sound)) {
|
||||
if (data.isType!(rl.Sound)) {
|
||||
rl.SetSoundPitch(data.get!(rl.Sound)(), value);
|
||||
} else {
|
||||
rl.SetMusicPitch(data.get!(rl.Music)(), value);
|
||||
|
@ -484,7 +484,7 @@ struct Sound {
|
|||
/// Sets the stereo panning of the sound.
|
||||
@trusted
|
||||
void setPan(float value) {
|
||||
if (data.isKind!(rl.Sound)) {
|
||||
if (data.isType!(rl.Sound)) {
|
||||
rl.SetSoundPan(data.get!(rl.Sound)(), value);
|
||||
} else {
|
||||
rl.SetMusicPan(data.get!(rl.Music)(), value);
|
||||
|
@ -495,7 +495,7 @@ struct Sound {
|
|||
@trusted
|
||||
void free() {
|
||||
if (isEmpty) return;
|
||||
if (data.isKind!(rl.Sound)) {
|
||||
if (data.isType!(rl.Sound)) {
|
||||
rl.UnloadSound(data.get!(rl.Sound)());
|
||||
} else {
|
||||
rl.UnloadMusicStream(data.get!(rl.Music)());
|
||||
|
@ -1947,7 +1947,7 @@ void playSound(Sound sound) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (sound.data.isKind!(rl.Sound)) {
|
||||
if (sound.data.isType!(rl.Sound)) {
|
||||
rl.PlaySound(sound.data.get!(rl.Sound)());
|
||||
} else {
|
||||
rl.PlayMusicStream(sound.data.get!(rl.Music)());
|
||||
|
@ -1967,7 +1967,7 @@ void stopSound(Sound sound) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (sound.data.isKind!(rl.Sound)) {
|
||||
if (sound.data.isType!(rl.Sound)) {
|
||||
rl.StopSound(sound.data.get!(rl.Sound)());
|
||||
} else {
|
||||
rl.StopMusicStream(sound.data.get!(rl.Music)());
|
||||
|
@ -1986,7 +1986,7 @@ void pauseSound(Sound sound) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (sound.data.isKind!(rl.Sound)) {
|
||||
if (sound.data.isType!(rl.Sound)) {
|
||||
rl.PauseSound(sound.data.get!(rl.Sound)());
|
||||
} else {
|
||||
rl.PauseMusicStream(sound.data.get!(rl.Music)());
|
||||
|
@ -2005,7 +2005,7 @@ void resumeSound(Sound sound) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (sound.data.isKind!(rl.Sound)) {
|
||||
if (sound.data.isType!(rl.Sound)) {
|
||||
rl.ResumeSound(sound.data.get!(rl.Sound)());
|
||||
} else {
|
||||
rl.ResumeMusicStream(sound.data.get!(rl.Music)());
|
||||
|
@ -2024,7 +2024,7 @@ void updateSound(Sound sound) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (sound.data.isKind!(rl.Music)) {
|
||||
if (sound.data.isType!(rl.Music)) {
|
||||
rl.UpdateMusicStream(sound.data.get!(rl.Music)());
|
||||
}
|
||||
}
|
||||
|
|
320
source/parin/story.d
Normal file
320
source/parin/story.d
Normal file
|
@ -0,0 +1,320 @@
|
|||
// TODO: skipValue might need some work. Not clear how splitting works.
|
||||
// TODO: toStr char arrays might need some work. It has bad error messages for them.
|
||||
// NOTE: Was about to add the dialogue stuff like jumping skipping...
|
||||
// NOTE: Remember to update both joka and parin at the same time because there was a evil change.
|
||||
|
||||
/// The `story` module provides a simple and versatile dialogue system.
|
||||
module parin.story;
|
||||
|
||||
import joka.types;
|
||||
import joka.containers;
|
||||
import joka.ascii;
|
||||
import joka.io;
|
||||
import joka.unions;
|
||||
|
||||
//Story Syntax:
|
||||
// # Comment
|
||||
// * Point
|
||||
// | Line
|
||||
// > Jump
|
||||
// ^ Menu
|
||||
// $ Expression
|
||||
|
||||
enum StoryOp : ubyte {
|
||||
ADD = '+',
|
||||
SUB = '-',
|
||||
MUL = '*',
|
||||
DIV = '/',
|
||||
MOD = '%',
|
||||
AND = '&',
|
||||
OR = '|',
|
||||
LESS = '<',
|
||||
GREATER = '>',
|
||||
EQUAL = '=',
|
||||
NOT = '!',
|
||||
POP = '~',
|
||||
SWAP,
|
||||
COPY,
|
||||
IF,
|
||||
THEN,
|
||||
END,
|
||||
ECHO,
|
||||
LEAK,
|
||||
GET,
|
||||
SET,
|
||||
INC,
|
||||
DEC,
|
||||
MENU,
|
||||
SKIP,
|
||||
JUMP,
|
||||
CALL,
|
||||
}
|
||||
|
||||
alias StoryWord = char[16];
|
||||
alias StoryNumber = long;
|
||||
alias StoryValueData = Variant!(StoryWord, StoryNumber);
|
||||
|
||||
struct StoryValue {
|
||||
StoryValueData data;
|
||||
|
||||
alias data this;
|
||||
|
||||
static foreach (Type; StoryValueData.Types) {
|
||||
this(Type value) {
|
||||
data = value;
|
||||
}
|
||||
}
|
||||
|
||||
IStr toStr() {
|
||||
static char[64] buffer = void;
|
||||
|
||||
auto result = buffer[];
|
||||
if (data.isType!long) {
|
||||
result.copy(data.get!long().toStr());
|
||||
} else {
|
||||
auto temp = data.get!(char[16])()[];
|
||||
foreach (i, c; temp) {
|
||||
if (temp[i] == 0) {
|
||||
temp = temp[0 .. i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.copy(temp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
struct StoryVariable {
|
||||
StoryWord name;
|
||||
StoryValue value;
|
||||
}
|
||||
|
||||
struct Story {
|
||||
LStr script;
|
||||
FixedList!(StoryValue, 8) stack;
|
||||
List!StoryVariable variables;
|
||||
int previousMenuResult;
|
||||
|
||||
Fault evaluate(IStr expression) {
|
||||
stack.clear();
|
||||
auto ifCounter = 0;
|
||||
while (true) {
|
||||
if (expression.length == 0) break;
|
||||
auto token = expression.skipValue(' ');
|
||||
if (token.length == 0) continue;
|
||||
if (ifCounter > 0) {
|
||||
if (token == StoryOp.THEN.toStr()) ifCounter -= 1;
|
||||
continue;
|
||||
}
|
||||
if (token.isMaybeStoryOp) {
|
||||
auto tempResult = token.toStoryOp();
|
||||
if (tempResult.isNone) return Fault.cantParse;
|
||||
auto op = tempResult.value;
|
||||
final switch (op) {
|
||||
case StoryOp.ADD:
|
||||
case StoryOp.SUB:
|
||||
case StoryOp.MUL:
|
||||
case StoryOp.DIV:
|
||||
case StoryOp.MOD:
|
||||
case StoryOp.AND:
|
||||
case StoryOp.OR:
|
||||
case StoryOp.LESS:
|
||||
case StoryOp.GREATER:
|
||||
case StoryOp.EQUAL:
|
||||
if (stack.length < 2) return Fault.invalid;
|
||||
auto db = stack.pop();
|
||||
auto da = stack.pop();
|
||||
if (!db.isType!long || !da.isType!long) return Fault.invalid;
|
||||
auto a = da.get!long;
|
||||
auto b = db.get!long;
|
||||
auto c = 0L;
|
||||
switch (op) {
|
||||
case StoryOp.ADD: c = b + a; break;
|
||||
case StoryOp.SUB: c = b - a; break;
|
||||
case StoryOp.MUL: c = b * a; break;
|
||||
case StoryOp.DIV: c = b / a; break;
|
||||
case StoryOp.MOD: c = b % a; break;
|
||||
case StoryOp.AND: c = b && a; break;
|
||||
case StoryOp.OR: c = b || a; break;
|
||||
case StoryOp.LESS: c = b < a; break;
|
||||
case StoryOp.GREATER: c = b > a; break;
|
||||
case StoryOp.EQUAL: c = b == a; break;
|
||||
default: assert(0, "TODO: {}".format(op));
|
||||
}
|
||||
stack.append(StoryValue(c));
|
||||
break;
|
||||
case StoryOp.NOT:
|
||||
if (stack.length < 1) return Fault.invalid;
|
||||
auto da = stack.pop();
|
||||
if (!da.isType!long) return Fault.invalid;
|
||||
stack.append(StoryValue(!da.get!long));
|
||||
break;
|
||||
case StoryOp.POP:
|
||||
if (stack.length < 1) return Fault.invalid;
|
||||
stack.pop();
|
||||
break;
|
||||
case StoryOp.SWAP:
|
||||
if (stack.length < 2) return Fault.invalid;
|
||||
auto db = stack.pop();
|
||||
auto da = stack.pop();
|
||||
stack.append(db);
|
||||
stack.append(da);
|
||||
break;
|
||||
case StoryOp.COPY:
|
||||
if (stack.length < 1) return Fault.invalid;
|
||||
stack.append(stack[$ - 1]);
|
||||
break;
|
||||
case StoryOp.IF:
|
||||
if (stack.length < 1) return Fault.invalid;
|
||||
auto da = stack.pop();
|
||||
if (!da.isType!long) return Fault.invalid;
|
||||
if (!da.get!long) ifCounter += 1;
|
||||
break;
|
||||
case StoryOp.THEN:
|
||||
break;
|
||||
case StoryOp.END:
|
||||
return Fault.none;
|
||||
case StoryOp.ECHO:
|
||||
if (stack.length) println(stack[$ - 1]);
|
||||
else println();
|
||||
break;
|
||||
case StoryOp.LEAK:
|
||||
print("[");
|
||||
foreach (i, item; stack) {
|
||||
auto space = " ";
|
||||
auto separator = ",";
|
||||
if (i == stack.length - 1) {
|
||||
space = "";
|
||||
separator = "";
|
||||
}
|
||||
print(item, separator, space);
|
||||
}
|
||||
println("]");
|
||||
break;
|
||||
case StoryOp.GET:
|
||||
if (stack.length < 1) return Fault.invalid;
|
||||
auto da = stack.pop();
|
||||
if (!da.isType!StoryWord) return Fault.invalid;
|
||||
auto a = da.get!StoryWord();
|
||||
auto isNotThere = true;
|
||||
foreach (variable; variables) {
|
||||
if (a == variable.name) {
|
||||
stack.append(variable.value);
|
||||
isNotThere = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isNotThere) return Fault.invalid;
|
||||
break;
|
||||
case StoryOp.SET:
|
||||
if (stack.length < 2) return Fault.invalid;
|
||||
auto db = stack.pop();
|
||||
auto da = stack.pop();
|
||||
if (!db.isType!StoryWord) return Fault.invalid;
|
||||
auto b = db.get!StoryWord();
|
||||
auto isNotThere = true;
|
||||
foreach (ref variable; variables) {
|
||||
if (b == variable.name) {
|
||||
variable.value = da;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isNotThere) variables.append(StoryVariable(b, da));
|
||||
break;
|
||||
case StoryOp.MENU:
|
||||
stack.append(StoryValue(previousMenuResult));
|
||||
break;
|
||||
case StoryOp.INC:
|
||||
case StoryOp.DEC:
|
||||
if (stack.length < 2) return Fault.invalid;
|
||||
auto db = stack.pop();
|
||||
auto da = stack.pop();
|
||||
if (!db.isType!StoryWord || !da.isType!StoryNumber) return Fault.invalid;
|
||||
auto b = db.get!StoryWord();
|
||||
auto a = da.get!StoryNumber();
|
||||
auto isNotThere = true;
|
||||
foreach (ref variable; variables) {
|
||||
if (b == variable.name) {
|
||||
if (variable.value.isType!StoryNumber) {
|
||||
variable.value.get!StoryNumber() += a * (op == StoryOp.INC ? 1 : -1);
|
||||
} else {
|
||||
return Fault.invalid;
|
||||
}
|
||||
isNotThere = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isNotThere) return Fault.invalid;
|
||||
break;
|
||||
case StoryOp.SKIP:
|
||||
case StoryOp.JUMP:
|
||||
case StoryOp.CALL:
|
||||
println("TODO: ", op);
|
||||
break;
|
||||
}
|
||||
} else if (token.isMaybeStoryNumber) {
|
||||
auto tempResult = token.toSigned();
|
||||
if (tempResult.isNone) return Fault.cantParse;
|
||||
stack.append(StoryValue(tempResult.value));
|
||||
} else if (token.isMaybeStoryWord) {
|
||||
char[16] temp = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
foreach (i, c; token) temp[i] = c;
|
||||
stack.append(StoryValue(temp));
|
||||
} else {
|
||||
return Fault.cantParse;
|
||||
}
|
||||
}
|
||||
return Fault.none;
|
||||
}
|
||||
}
|
||||
|
||||
bool isMaybeStoryOp(IStr value) {
|
||||
if (value.length == 0) return false;
|
||||
auto c = value[0];
|
||||
auto isSymbol = (c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~');
|
||||
if (isSymbol) {
|
||||
if (c == '_') return false;
|
||||
return value.length == 1;
|
||||
} else {
|
||||
return c.isUpper;
|
||||
}
|
||||
}
|
||||
|
||||
bool isMaybeStoryNumber(IStr value) {
|
||||
if (value.length == 0) return false;
|
||||
auto c = value[0];
|
||||
auto isSymbol = (c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~');
|
||||
if (isSymbol) {
|
||||
if (c == '_') return false;
|
||||
return value.length >= 2 && value[1].isDigit;
|
||||
} else {
|
||||
return c.isDigit;
|
||||
}
|
||||
}
|
||||
|
||||
bool isMaybeStoryWord(IStr value) {
|
||||
if (value.length == 0) return false;
|
||||
auto c = value[0];
|
||||
auto isSymbol = (c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~');
|
||||
return c == '_' || (!c.isUpper && !isSymbol);
|
||||
}
|
||||
|
||||
Result!StoryOp toStoryOp(IStr value) {
|
||||
switch (value) {
|
||||
case "+": return Result!StoryOp(StoryOp.ADD);
|
||||
case "-": return Result!StoryOp(StoryOp.SUB);
|
||||
case "*": return Result!StoryOp(StoryOp.MUL);
|
||||
case "/": return Result!StoryOp(StoryOp.DIV);
|
||||
case "%": return Result!StoryOp(StoryOp.MOD);
|
||||
case "&": return Result!StoryOp(StoryOp.AND);
|
||||
case "|": return Result!StoryOp(StoryOp.OR);
|
||||
case "<": return Result!StoryOp(StoryOp.LESS);
|
||||
case ">": return Result!StoryOp(StoryOp.GREATER);
|
||||
case "=": return Result!StoryOp(StoryOp.EQUAL);
|
||||
case "!": return Result!StoryOp(StoryOp.NOT);
|
||||
case "~": return Result!StoryOp(StoryOp.POP);
|
||||
default: break;
|
||||
}
|
||||
return toEnum!StoryOp(value);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue