diff --git a/src/main.ts b/src/main.ts
index 4d8e752..edbe858 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,13 +1,47 @@
import { Lexer } from './parser/Lexer'
-import { BinaryExpression } from './parser/ast/BinaryExpression'
-import { Expression } from './parser/ast/Expression'
-import { Operator } from './parser/ast/Operator'
-import { ValueExpression } from './parser/ast/ValueExpression'
+import { Parser } from './parser/Parser'
+import { MainView } from './view/MainView'
-let expr1: Expression = new ValueExpression(10)
-let expr2: Expression = new ValueExpression(20)
-let exprAdd: Expression = new BinaryExpression(Operator.ADD, expr1, expr2)
-console.log(exprAdd.eval());
+function run(data: string): void {
+ const tokens = new Lexer(data).process().getTokens();
+ const parser = new Parser(tokens);
+ const view = new MainView(parser, false);
+ parser.setView(view);
+ view.init();
+ parser.next();
+}
-const tokens = new Lexer("10 / 2").process().getTokens();
-console.log(tokens)
\ No newline at end of file
+document.addEventListener('DOMContentLoaded', function() {
+ run(`scene bg black
+ "Основные теги:\n{b}жирный{/b} {i}курсив{/i} {u}подчёркнутый{/u}"
+ "{big}большой{/big} нормальный {small}маленький{/small}"
+ "{center}текст по центру"
+ sl "{center}текст по центру"
+ "{html}{center}
HTML
"
+ "{html}Большой big"
+ "{html}Маленький small"
+ "{html}Жирный bold strong"
+ "{html}Курсив i em cite dfn"
+ "{html}Подчёркнутый"
+ "{html}Длинная цитата
blockquote
"
+ "{html}
Переносы
строк
br
"
+ "{html}Моноширинный tt"
+ "{html}Подстрочный sub Нормальный Надстрочный sup"
+ '{html}Ссылка a href'
+ "{html}Заголовок h1
+ Заголовок h2
+ Заголовок h3
+ Заголовок h4
+ Заголовок h5
+ Заголовок h6
"
+ '{html}font color="red"
+ font color="purple"
+ font color="#336699"
+ font color="#ffffff"'
+ '{html}font face="serif"
+ font face="sans-serif"
+ font face="monospace"'
+ '{html}font face="monospace" color="#7f7f7f"'
+ "{html}Параграф
p
"
+ "{html}Блок div
"`)
+});
\ No newline at end of file
diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts
index 1ead9a6..6e7c7df 100644
--- a/src/parser/Parser.ts
+++ b/src/parser/Parser.ts
@@ -34,10 +34,7 @@ export class Parser implements Navigable {
/** Optimization, if there no endmenu/endif => don't search them */
this.hasEndMenu = false;
this.hasEndIf = false;
- }
-
- public getInstance(): Parser {
- return this;
+ this.preScan();
}
public setView(view: ViewModel): void {
diff --git a/src/utils/TextUtils.ts b/src/utils/TextUtils.ts
index e5fb9a9..25ddbb0 100644
--- a/src/utils/TextUtils.ts
+++ b/src/utils/TextUtils.ts
@@ -1,7 +1,19 @@
export class TextUtils {
- public static isEmpty(str: string|null) {
+ public static isEmpty(str: string|null): boolean {
if (typeof str === 'undefined') return true;
if (str === undefined) return true;
return str.length === 0;
}
+
+ public static equalsIgnoreCase(str: string, text: string): boolean {
+ return str.toLowerCase() == text.toLowerCase();
+ }
+
+ public static contains(str: string, text: string): boolean {
+ return str.indexOf(text) >= 0;
+ }
+
+ public static replaceAll(str: string, find: string, replace: string): string {
+ return str.replace(new RegExp(find, 'g'), replace);
+ }
}
\ No newline at end of file
diff --git a/src/view/MainView.ts b/src/view/MainView.ts
new file mode 100644
index 0000000..39b0d12
--- /dev/null
+++ b/src/view/MainView.ts
@@ -0,0 +1,311 @@
+import { TextUtils } from "../utils/TextUtils";
+import { Characters } from "./model/Characters";
+import { FadeInfo } from "./model/FadeInfo";
+import { Menu } from "./model/Menu";
+import { Navigable } from "./model/Navigable";
+import { Transition } from "./model/Transition";
+import { TransitionType } from "./model/TransitionType";
+import { ViewModel } from "./model/ViewModel";
+
+export class MainView implements ViewModel {
+ private static readonly NO_FADE = new FadeInfo();
+
+ private windowTag: HTMLElement;
+ private textAuthorTag: HTMLElement;
+ private textContentTag: HTMLElement;
+
+ private musicPlayerAudio: HTMLAudioElement;
+ private soundPlayerAudio: HTMLAudioElement;
+ private soundLoopPlayerAudio: HTMLAudioElement;
+ private ambiencePlayerAudio: HTMLAudioElement;
+ private musicQueue: Array;
+ private soundQueue: Array;
+
+ private backgroundName: string;
+ private backgroundType: string;
+ private characters: Characters;
+
+ private useSpriteTransitions: boolean;
+ private spriteInContainer: {};
+
+ private blockTap: boolean;
+ private cancelNextStep: boolean;
+ private nextCommandRunnable: () => void;
+
+ constructor(private navigable: Navigable, private debug: boolean = false) {
+ this.windowTag = document.getElementById('window');
+ this.textAuthorTag = document.getElementById('textAuthor');
+ this.textContentTag = document.getElementById('textContent');
+
+ this.musicPlayerAudio = new Audio();
+ this.soundPlayerAudio = new Audio();
+ this.soundLoopPlayerAudio = new Audio();
+ this.ambiencePlayerAudio = new Audio();
+ this.musicQueue = new Array();
+ this.soundQueue = new Array();
+
+ this.backgroundName = "";
+ this.backgroundType = "";
+ this.characters = new Characters();
+ this.characters.makeNamesUnknown();
+ this.characters.makeNamesKnown();
+
+ // this.places = new MapPlaces(); // TODO
+
+ this.useSpriteTransitions = true;
+ this.spriteInContainer = {};
+
+ this.blockTap = false;
+ this.cancelNextStep = false;
+ this.nextCommandRunnable = () => {
+ this.blockTap = false;
+ if (!this.cancelNextStep) navigable.next();
+ };
+ }
+
+ public init(): void {
+ document.getElementById('menu').style.display = 'none';
+ document.getElementById('mainMenuButton').onclick = (e) => this.showMainMenu();
+ document.getElementById('background').onclick = (e) => this.onTouch(e);
+ }
+
+ private onTouch(e: MouseEvent): void {
+ if (this.blockTap) return;
+ if (document.getElementById('menu').style.display !== 'none') return;
+ if ((e.target as HTMLElement).tagName.toLowerCase() === 'li') return;
+ if ((e.target as HTMLElement).id === 'mainMenuButton') return;
+
+ this.cancelNextStep = true;
+ this.navigable.next();
+ }
+
+ private showMainMenu(): void {
+ }
+
+ public text(text: string): void {
+ this.textAuthorTag.innerText = '';
+ if (TextUtils.isEmpty(text)) this.windowHide("");
+ else {
+ this.windowShow("");
+ this.formatString(this.textContentTag, text);
+ }
+ }
+
+ public textW(whoid: string, text: string): void {
+ if (TextUtils.equalsIgnoreCase(whoid, 'th')) this.text("~ " + text + " ~");
+ else if (!this.characters.contains(whoid)) this.text(text);
+ else {
+ this.windowShow("");
+ const person = this.characters.get(whoid);
+ this.textAuthorTag.textContent = person.name;
+ this.textAuthorTag.style.color = this.toColor(person.color);
+ this.formatString(this.textContentTag, text);
+ }
+ }
+
+ private formatString(tag: HTMLElement, text: string) {
+ let edited = TextUtils.replaceAll(text, "\\{w.*?\\}", "");
+ if (TextUtils.contains(edited, "{center}")) {
+ edited = TextUtils.replaceAll(edited, "\\{center\\}", "");
+ this.textContentTag.style.textAlign = "center";
+ } else {
+ this.textContentTag.style.textAlign = "left";
+ }
+ if (TextUtils.contains(edited, "{html}")) {
+ edited = TextUtils.replaceAll(edited, "\\{html\\}", "");
+ tag.innerHTML = edited;
+ return;
+ }
+ const codes = ["b", "i", "s", "u", "big", "small"];
+ let html = false;
+ for (let i = 0; i < codes.length; i++) {
+ const ch = codes[i];
+ if (TextUtils.contains(edited, "{" + ch + "}")) {
+ edited = TextUtils.replaceAll(edited, "{" + ch + "}", "<" + ch + ">");
+ edited = TextUtils.replaceAll(edited, "{/" + ch + "}", "" + ch + ">");
+ html = true;
+ }
+ }
+ if (html) {
+ edited = edited.replace("\n", "
");
+ tag.innerHTML = edited;
+ } else {
+ tag.textContent = edited;
+ // TODO
+ // const tmp = $(tag).text(edited);
+ // tmp.html(tmp.html().replace(/\n/g, '
'));
+ }
+ }
+
+ public background(type: string, name: string, effect: string): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public sprite(whoid: string, params: string, position: string, alias: string, effect: string): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public hideSprite(whoid: string, effect: string): void {
+ if (!(whoid in this.spriteInContainer)) return;
+ this.hide(whoid, effect);
+ }
+
+ private hide(whoid: string, effect: string): void {
+ const img = this.spriteInContainer[whoid];
+ if (img == null) return;
+ if (effect in Transition.DEFAULT) {
+ const transition = Transition.DEFAULT[effect];
+ if (transition["type"] === TransitionType.TYPE_FADE) {
+ // TODO
+ //img.fadeOut(transition["outTime"], function () {
+ // $(this).remove();
+ //});
+ img.remove();
+ } else {
+ img.remove();
+ }
+ } else {
+ img.remove();
+ }
+ delete this.spriteInContainer[whoid];
+ }
+
+ private spritesClear() : void {
+ document.getElementById('container').innerHTML = '';
+ this.spriteInContainer = {};
+ }
+
+ public menu(menu: Menu): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public showMap(): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public setZone(zone: string, label: string): void {
+ // this.places.setZone(name, label);
+ }
+
+ public resetZone(zone: string): void {
+ // this.places.resetZone(zone);
+ }
+
+ public disableCurrentZone(): void {
+ // this.places.disableCurrentZone();
+ }
+
+ public disableAllZones(): void {
+ // this.places.disableAllZones();
+ }
+
+ public makeNamesKnown(): void {
+ this.characters.makeNamesKnown();
+ }
+
+ public makeNamesUnknown(): void {
+ this.characters.makeNamesUnknown();
+ }
+
+ public meet(whoid: string, name: string): void {
+ this.characters.setName(whoid, name);
+ }
+
+ public music(name: string, fade: FadeInfo): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public ambience(name: string, fade: FadeInfo): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public sound(name: string, fade: FadeInfo): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public soundLoop(name: string, fade: FadeInfo): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public addSoundToQueue(name: string): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public addMusicToQueue(name: string): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public stopAmbience(fade: FadeInfo): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public stopSoundLoop(fade: FadeInfo): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public stopSound(fade: FadeInfo): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public stopMusic(fade: FadeInfo): void {
+ throw new Error("Method not implemented.");
+ }
+
+ public windowShow(effect: string): boolean {
+ if (this.windowTag.style.visibility !== "visible") {
+ this.windowTag.style.visibility = "visible";
+ }
+ if (!TextUtils.isEmpty(effect)) {
+ this.background(this.backgroundType, this.backgroundName, effect);
+ return true;
+ }
+ return false;
+ }
+
+ public windowHide(effect: string): boolean {
+ if (this.windowTag.style.visibility !== "hidden") {
+ this.windowTag.style.visibility = "hidden";
+ }
+ if (!TextUtils.isEmpty(effect)) {
+ this.background(this.backgroundType, this.backgroundName, effect);
+ return true;
+ }
+ return false;
+ }
+
+ private windowSwitchVisibility(): void {
+ if (this.windowTag.style.visibility === "hidden") {
+ this.windowTag.style.visibility = "visible";
+ } else {
+ this.windowTag.style.visibility = "hidden";
+ }
+ }
+
+ public pause(duration: number, isHard: boolean): void {
+ this.blockTap = isHard;
+ this.cancelNextStep = false;
+ setTimeout(this.nextCommandRunnable, duration);
+ }
+
+ public finish(): void {
+ if (this.debug) {
+ this.stopMusic(MainView.NO_FADE);
+ this.stopSound(MainView.NO_FADE);
+ this.stopSoundLoop(MainView.NO_FADE);
+ this.stopAmbience(MainView.NO_FADE);
+ this.spritesClear();
+ this.text('{center}{html}Сценарий завершён');
+ } else {
+ this.text('{center}{html}Вернуться на главную');
+ }
+ }
+
+ private toColor(num: number): string {
+ num >>>= 0;
+ const b = num & 0xFF,
+ g = (num & 0xFF00) >>> 8,
+ r = (num & 0xFF0000) >>> 16,
+ a = ( (num & 0xFF000000) >>> 24 ) / 255 ;
+ return "rgba(" + [r, g, b, a].join(",") + ")";
+ }
+}
\ No newline at end of file
diff --git a/src/view/model/ViewModel.ts b/src/view/model/ViewModel.ts
index ec97625..5bc36dc 100644
--- a/src/view/model/ViewModel.ts
+++ b/src/view/model/ViewModel.ts
@@ -4,7 +4,6 @@ import { Menu } from "./Menu"
export interface ViewModel {
text(text: string): void
textW(whoid: string, text: string): void
- meet(whoid: string, name: string): void
background(type: string, name: string, effect: string): void
sprite(whoid: string, params: string, position: string, alias: string, effect: string): void
hideSprite(whoid: string, effect: string): void
@@ -12,12 +11,13 @@ export interface ViewModel {
menu(menu: Menu): void
showMap(): void
setZone(zone: string, label: string): void
+ resetZone(zone: string): void
disableCurrentZone(): void
disableAllZones(): void
- resetZone(zone: string): void
- makeNamesUnknown(): void
+ meet(whoid: string, name: string): void
makeNamesKnown(): void
+ makeNamesUnknown(): void
music(name: string, fade: FadeInfo): void
ambience(name: string, fade: FadeInfo): void