Skip to content

Commit

Permalink
Merge pull request #271 from RSamaium/v4.1.3
Browse files Browse the repository at this point in the history
V4.1.3
  • Loading branch information
RSamaium authored Nov 17, 2023
2 parents 9e5c8ba + afcd889 commit e7da660
Show file tree
Hide file tree
Showing 24 changed files with 391 additions and 29 deletions.
15 changes: 15 additions & 0 deletions docs/.vitepress/theme/components/Playground.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<iframe :src="path" loading="lazy" scrolling="no" style="border:0;height:760px;max-width:800px;width:100%">
</iframe>
</template>

<script>
export default {
props: ['id'],
computed: {
path() {
return `https://playground.rpgjs.dev/sandbox/${this.id}`
}
}
}
</script>
3 changes: 2 additions & 1 deletion docs/.vitepress/theme/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { h } from 'vue'
import { VPTheme } from '@vue/theme'
import DefaultTheme from 'vitepress/theme'
import PathTo from './components/PathTo.vue'
import Video from './components/Video.vue'
import Type from './components/Type.vue'
import PreferenceSwitch from './components/PreferenceSwitch.vue'
import Playground from './components/Playground.vue'

export default {
extends: DefaultTheme,
Expand All @@ -15,6 +15,7 @@ export default {
})
},
enhanceApp(ctx) {
ctx.app.component('Playground', Playground)
ctx.app.component('PathTo', PathTo)
ctx.app.component('Video', Video)
ctx.app.component('Type', Type)
Expand Down
4 changes: 4 additions & 0 deletions docs/gui/tooltip.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
- Know how to create a GUI and add it in the module
- Be comfortable with VueJS

## Preview

<Playground id="232" />

## Goal

This is very useful to make more advanced interactions on a sprite. For example, display a tooltip or additional interactive displays.
Expand Down
7 changes: 6 additions & 1 deletion docs/guide/create-event.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ export class CharaEvent extends RpgEvent {
> You have to know that there are several commands, here we just display a text. In fact, it opens a predefined GUI.
> We use `async/await` because we wait for the player to press Enter after the dialog to continue the other instructions.
<Playground id="225" />

## Example 2: Example: Earn gold

Here is a more complete example. It shows a small scenario. It is important to know that this event will be seen by everyone. However, the action will be independent to each player :
Expand Down Expand Up @@ -239,4 +241,7 @@ export class DoorEvent extends RpgEvent {

With, `onChanges`, a listening is done on a potential change. You can then update the door graphics according to the state of the `LEVER` variable

> Note that in MMORPG mode, everyone sees the change of the door graphics even if the variable is changed only for the player. Switch the event to Scenario mode if you want to make a single event per player
> Note that in MMORPG mode, everyone sees the change of the door graphics even if the variable is changed only for the player. Switch the event to Scenario mode if you want to make a single event per player

<Playground id="228" />
11 changes: 6 additions & 5 deletions docs/guide/create-gui.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@
1. Each user interface uses the DOM and not HTML5 Canvas. Why not? Because it is easier to create interfaces with components than with canvas graphics.
2. Each GUI has an identifier. Predefined identifiers exist to manage existing GUI (dialog, main menu, shop, etc.).

## Goal
## Preview

We want to create a life bar. It will remain in hauy on the left side of the screen.

![Hud](/assets/hud.png)

<Playground id="222" />

## Component

Expand Down Expand Up @@ -125,15 +124,17 @@ export default {
<PathTo to="playerFile" />

```ts
import { RpgPlayer, RpgPlayerHooks } from '@rpgjs/server'
import { RpgPlayer, type RpgPlayerHooks } from '@rpgjs/server'

export const player: RpgPlayerHooks = {
const player: RpgPlayerHooks = {
onJoinMap(player: RpgPlayer) {
player.gui('my-tooltip').open()
player.showAttachedGui()
// you can hide with player.hideAttachedGui()
}
}

export default player
```

We open the `my-tooltip` GUI and display the player's tooltip
Expand Down
4 changes: 4 additions & 0 deletions docs/guide/create-world-maps.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ The interest is to teleport the player on an adjacent map on consistent X and Y

<Video src="/assets/rpgjs_world.mp4" />

## Preview

<Playground id="224" />

## Preparing the world in the editor

Click on `World > New World` and add a file ending with the extension `.world` in <Path to="tmxDir" />
Expand Down
34 changes: 33 additions & 1 deletion packages/compiler/tests/toml.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import configTomlPlugin, { formatVariableName, getAllFiles, importString, transformPathIfModule, searchFolderAndTransformToImportString, loadSpriteSheet, loadClientFiles, createModuleLoad, } from '../src/build/vite-plugin-config.toml'
import configTomlPlugin, { formatVariableName, getAllFiles, importString, transformPathIfModule, searchFolderAndTransformToImportString, loadSpriteSheet, loadClientFiles, createModuleLoad, loadServerFiles, } from '../src/build/vite-plugin-config.toml'
import Vi, { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'
import * as path from 'path'
import mockFs from 'mock-fs'
Expand Down Expand Up @@ -383,4 +383,36 @@ describe('createModuleLoad', () => {
const result = createModuleLoad(id, 'variableName', '', {}, {});
expect(result).toContain("import mod from '@/modules/somepath/index.ts'");
});

test('Test database', () => {
const fakeDatabase = {}

const nb = 10

for (let i = 0; i < nb; i++) {
fakeDatabase[`file${i}.ts`] = '"export default class Actor {}'
}

mockFs({
'main': {
'database': {
'actors': fakeDatabase
}
}
});

const result = loadServerFiles('main', {
modulesCreated: []
}, {});

let str = ''
for (let i = 0; i < nb; i++) {
expect(result).toContain(`import _main_database_actors_file${i}ts from './main/database/actors/file${i}.ts'`)
str += `_main_database_actors_file${i}ts,`
}

str = str.slice(0, -1) // remove last comma

expect(result).toContain(`database: [${str}]`)
});
})
41 changes: 41 additions & 0 deletions packages/plugins/interpreter/src/blocks/gold.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { RpgPlayer } from "@rpgjs/server";
import type { Block } from "../types/block";
import { Group } from "../types/group";

export type ChangeGold = {
id: 'gold'
value: number
}

export default (formatMessage): Block => ({
id: 'gold',
group: Group.Param,
title: formatMessage({
defaultMessage: 'Show Choices',
description: 'Choices block',
id: 'block.choice'
}),
description: formatMessage({
defaultMessage: 'Display a choice',
description: 'Choices block description',
id: 'block.choice.description'
}),
display: ['gold'],
schema: {
type: 'object',
properties: {
value: {
type: 'number',
title: formatMessage({
defaultMessage: 'Value',
description: 'Set Value',
}),
}
},
required: ['value']
},
async execute(player: RpgPlayer, dataBlock: ChangeGold) {
player.gold += dataBlock.value
return undefined
}
})
7 changes: 5 additions & 2 deletions packages/plugins/interpreter/src/blocks/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import choice from './choice'
import text from './text'
import gold from './gold'
import type { Choice } from './choice'
import type { Text } from './text'
import type { ChangeGold } from './gold'

export type Flow = {
[blockId: string]: Choice | Text
[blockId: string]: Choice | Text | ChangeGold
}

export default [
choice,
text
text,
gold
]
32 changes: 25 additions & 7 deletions packages/plugins/interpreter/src/interpreter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Observable, of, switchMap, from, throwError, timeout, map } from "rxjs"
import type { Block, BlockExecuteReturn } from "./types/block"
import type { Block, BlockExecuteReturn, Parameters } from "./types/block"
import type { Edge } from "./types/edge"
import { formatMessage } from "./format-message"
import blocks, { Flow } from "./blocks"
Expand All @@ -13,18 +13,21 @@ export class RpgInterpreter {
private recursionLimit = 1000; // set the recursion limit
private currentRecursionDepth = 0; // track the current recursion depth
private executionTimeout = 5000; // set the execution timeout in milliseconds
private parametersSchema?: Parameters

constructor(
private dataBlocks: Flow,
private edges: Edge,
options: {
recursionLimit?: number;
executionTimeout?: number;
parametersSchema?: (formatMessage?) => Parameters
} = {},
_formatMessage = formatMessage
) {
this.recursionLimit = options.recursionLimit || this.recursionLimit;
this.executionTimeout = options.executionTimeout || this.executionTimeout;
this.parametersSchema = options.parametersSchema?.(_formatMessage)
blocks.forEach(block => {
const _blocks = block(_formatMessage)
this.blocksById[_blocks.id] = _blocks
Expand All @@ -44,18 +47,33 @@ export class RpgInterpreter {
*
* @return {Observable<any>} The RPG interpreter execution as an Observable.
*/
start({ player, blockId }: { player: RpgPlayer, blockId: string }): Observable<any> {
const validate = this.validateFlow(blockId)
if (validate === null) {
return this.executeFlow(player, blockId)
start({ player, blockId, parameters }: { player: RpgPlayer, blockId: string, parameters?}): Observable<any> {
const validateParams = this.validateParameters(parameters)
if (validateParams === null) {
const validate = this.validateFlow(blockId)
if (validate === null) {
return this.executeFlow(player, blockId, parameters)
}
return throwError(() => validate)
}
return throwError(() => validate)
return throwError(() => validateParams)
}

getHistory() {
return this.executionHistory
}

validateParameters(parameters: any): ZodError | null {
if (this.parametersSchema) {
const schemaZod = z.object(jsonSchemaToZod(this.parametersSchema));
const parse = schemaZod.safeParse(parameters);
if (!parse.success) {
return parse.error
}
}
return null
}

/**
* Validate a block.
*
Expand Down Expand Up @@ -183,7 +201,7 @@ export class RpgInterpreter {
*
* @return {Observable<any>} The flow execution as an Observable.
*/
private executeFlow(player: RpgPlayer, blockId: string): Observable<any> {
private executeFlow(player: RpgPlayer, blockId: string, parameters?: Parameters): Observable<any> {
// Prevent recursion beyond the limit
if (this.currentRecursionDepth++ > this.recursionLimit) {
return throwError(() => new Error('Recursion limit exceeded'));
Expand Down
2 changes: 2 additions & 0 deletions packages/plugins/interpreter/src/types/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface Block {
execute: (player: RpgPlayer, dataBlock: any) => BlockExecute;
}

export interface Parameters extends SchemaInterface {}

interface DisplayItem {
key: string;
hasHandles: boolean;
Expand Down
1 change: 1 addition & 0 deletions packages/plugins/interpreter/src/types/group.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export enum Group {
UI = 'ui',
Logic = 'logic',
Param = 'param',
}
3 changes: 3 additions & 0 deletions packages/plugins/interpreter/src/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { z, ZodType } from "zod";
const percent = z.number().min(0).max(100)

const typeMap: Record<string, (property?: any, key?: string) => z.ZodType<any, any>> = {
'code': () => z.string(),
'color': () => z.string().regex(/^#(?:[0-9a-fA-F]{3}){1,2}$/, 'Invalid color format'),
'date': () => z.date(),
'password': () => z.string(),
'email': () => z.string().email(),
'text': () => z.string(),
Expand Down
Loading

0 comments on commit e7da660

Please sign in to comment.