Add more main view implementations
This commit is contained in:
parent
1afa39bc04
commit
860398a340
54
src/main.ts
54
src/main.ts
@ -1,13 +1,47 @@
|
|||||||
import { Lexer } from './parser/Lexer'
|
import { Lexer } from './parser/Lexer'
|
||||||
import { BinaryExpression } from './parser/ast/BinaryExpression'
|
import { Parser } from './parser/Parser'
|
||||||
import { Expression } from './parser/ast/Expression'
|
import { MainView } from './view/MainView'
|
||||||
import { Operator } from './parser/ast/Operator'
|
|
||||||
import { ValueExpression } from './parser/ast/ValueExpression'
|
|
||||||
|
|
||||||
let expr1: Expression = new ValueExpression(10)
|
function run(data: string): void {
|
||||||
let expr2: Expression = new ValueExpression(20)
|
const tokens = new Lexer(data).process().getTokens();
|
||||||
let exprAdd: Expression = new BinaryExpression(Operator.ADD, expr1, expr2)
|
const parser = new Parser(tokens);
|
||||||
console.log(exprAdd.eval());
|
const view = new MainView(parser, false);
|
||||||
|
parser.setView(view);
|
||||||
|
view.init();
|
||||||
|
parser.next();
|
||||||
|
}
|
||||||
|
|
||||||
const tokens = new Lexer("10 / 2").process().getTokens();
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
console.log(tokens)
|
run(`scene bg black
|
||||||
|
"Основные теги:\n{b}жирный{/b} {i}курсив{/i} {u}подчёркнутый{/u}"
|
||||||
|
"{big}большой{/big} нормальный {small}маленький{/small}"
|
||||||
|
"{center}текст по центру"
|
||||||
|
sl "{center}текст по центру"
|
||||||
|
"{html}{center}<h1>HTML</h1>"
|
||||||
|
"{html}<big>Большой big</big>"
|
||||||
|
"{html}<small>Маленький small</small>"
|
||||||
|
"{html}<b>Жирный bold</b> <strong>strong</strong>"
|
||||||
|
"{html}<i>Курсив i</i> <em>em</em> <cite>cite</cite> <dfn>dfn</dfn>"
|
||||||
|
"{html}<u>Подчёркнутый</u>"
|
||||||
|
"{html}<blockquote><p>Длинная цитата</p>blockquote</blockquote>"
|
||||||
|
"{html}<br/><br/>Переносы<br/><br/>строк<br/><br/><br/><br/>br<br/>"
|
||||||
|
"{html}<tt>Моноширинный tt</tt>"
|
||||||
|
"{html}<sub>Подстрочный sub</sub> Нормальный <sup>Надстрочный sup</sup>"
|
||||||
|
'{html}<a href="http://rpy.annimon.com/">Ссылка a href</a>'
|
||||||
|
"{html}<h1>Заголовок h1</h1>
|
||||||
|
<h2>Заголовок h2</h2>
|
||||||
|
<h3>Заголовок h3</h3>
|
||||||
|
<h4>Заголовок h4</h4>
|
||||||
|
<h5>Заголовок h5</h5>
|
||||||
|
<h6>Заголовок h6</h6>"
|
||||||
|
'{html}<font color="red">font color="red"</font>
|
||||||
|
<font color="purple">font color="purple"</font>
|
||||||
|
<font color="#336699">font color="#336699"</font>
|
||||||
|
<font color="#ffffff">font color="#ffffff"</font>'
|
||||||
|
'{html}<font face="serif">font face="serif"</font>
|
||||||
|
<font face="sans-serif">font face="sans-serif"</font>
|
||||||
|
<font face="monospace">font face="monospace"</font>'
|
||||||
|
'{html}<font face="monospace" color="#7f7f7f">font face="monospace" color="#7f7f7f"</font>'
|
||||||
|
"{html}<p>Параграф</p><p>p</p>"
|
||||||
|
"{html}<div>Блок div</div>"`)
|
||||||
|
});
|
@ -34,10 +34,7 @@ export class Parser implements Navigable {
|
|||||||
/** Optimization, if there no endmenu/endif => don't search them */
|
/** Optimization, if there no endmenu/endif => don't search them */
|
||||||
this.hasEndMenu = false;
|
this.hasEndMenu = false;
|
||||||
this.hasEndIf = false;
|
this.hasEndIf = false;
|
||||||
}
|
this.preScan();
|
||||||
|
|
||||||
public getInstance(): Parser {
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setView(view: ViewModel): void {
|
public setView(view: ViewModel): void {
|
||||||
|
@ -1,7 +1,19 @@
|
|||||||
export class TextUtils {
|
export class TextUtils {
|
||||||
public static isEmpty(str: string|null) {
|
public static isEmpty(str: string|null): boolean {
|
||||||
if (typeof str === 'undefined') return true;
|
if (typeof str === 'undefined') return true;
|
||||||
if (str === undefined) return true;
|
if (str === undefined) return true;
|
||||||
return str.length === 0;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
311
src/view/MainView.ts
Normal file
311
src/view/MainView.ts
Normal file
@ -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<string>;
|
||||||
|
private soundQueue: Array<string>;
|
||||||
|
|
||||||
|
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", "<br/>");
|
||||||
|
tag.innerHTML = edited;
|
||||||
|
} else {
|
||||||
|
tag.textContent = edited;
|
||||||
|
// TODO
|
||||||
|
// const tmp = $(tag).text(edited);
|
||||||
|
// tmp.html(tmp.html().replace(/\n/g, '<br/>'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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}<span style="color: white">Сценарий завершён</span>');
|
||||||
|
} else {
|
||||||
|
this.text('{center}{html}<a style="color: white" href="/">Вернуться на главную</a>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(",") + ")";
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,6 @@ import { Menu } from "./Menu"
|
|||||||
export interface ViewModel {
|
export interface ViewModel {
|
||||||
text(text: string): void
|
text(text: string): void
|
||||||
textW(whoid: string, text: string): void
|
textW(whoid: string, text: string): void
|
||||||
meet(whoid: string, name: string): void
|
|
||||||
background(type: string, name: string, effect: string): void
|
background(type: string, name: string, effect: string): void
|
||||||
sprite(whoid: string, params: string, position: string, alias: string, effect: string): void
|
sprite(whoid: string, params: string, position: string, alias: string, effect: string): void
|
||||||
hideSprite(whoid: string, effect: string): void
|
hideSprite(whoid: string, effect: string): void
|
||||||
@ -12,12 +11,13 @@ export interface ViewModel {
|
|||||||
menu(menu: Menu): void
|
menu(menu: Menu): void
|
||||||
showMap(): void
|
showMap(): void
|
||||||
setZone(zone: string, label: string): void
|
setZone(zone: string, label: string): void
|
||||||
|
resetZone(zone: string): void
|
||||||
disableCurrentZone(): void
|
disableCurrentZone(): void
|
||||||
disableAllZones(): void
|
disableAllZones(): void
|
||||||
resetZone(zone: string): void
|
|
||||||
|
|
||||||
makeNamesUnknown(): void
|
meet(whoid: string, name: string): void
|
||||||
makeNamesKnown(): void
|
makeNamesKnown(): void
|
||||||
|
makeNamesUnknown(): void
|
||||||
|
|
||||||
music(name: string, fade: FadeInfo): void
|
music(name: string, fade: FadeInfo): void
|
||||||
ambience(name: string, fade: FadeInfo): void
|
ambience(name: string, fade: FadeInfo): void
|
||||||
|
Loading…
Reference in New Issue
Block a user