Add more main view implementations

This commit is contained in:
aNNiMON 2024-03-09 22:30:10 +02:00
parent 1afa39bc04
commit 860398a340
5 changed files with 372 additions and 18 deletions

View File

@ -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)
document.addEventListener('DOMContentLoaded', function() {
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>"`)
});

View File

@ -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 {

View File

@ -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);
}
}

311
src/view/MainView.ts Normal file
View 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(",") + ")";
}
}

View File

@ -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