├── .dockerignore ├── .github └── workflows │ ├── docker-publish.yml │ └── windows-publish.yml ├── .gitignore ├── .gitmodules ├── Dockerfile ├── HOW-TO-PLAY.md ├── Justfile ├── LICENSE ├── README.md ├── bin ├── doc ├── mtail ├── plain ├── start ├── start-ui └── tmux-ui ├── data └── .gitkeep ├── docs ├── DIRECTORY.md └── tmux.md ├── etc └── ui-settings.tin ├── framework ├── class.tin ├── doc.tin ├── log.tin ├── main.tin ├── module-loader.tin ├── multi-mud.tin ├── online.tin ├── settings.tin └── utils.tin ├── ids ├── DEFAULT ├── EXAMPLE ├── paotin ├── pkuxkx └── thuxyj ├── init.tin ├── init.vim ├── install.nsi ├── log └── .gitkeep ├── mud ├── pkuxkx │ ├── etc │ │ ├── ui-chat.tin │ │ └── ui-settings.extra.tin │ ├── framework │ │ └── online.extra.tin │ └── plugins │ │ ├── basic │ │ ├── busy.tin │ │ ├── char │ │ │ ├── __init__.tin │ │ │ ├── gmcp.tin │ │ │ ├── hp.tin │ │ │ ├── i2.tin │ │ │ ├── jifa.tin │ │ │ ├── sachet.tin │ │ │ ├── score.tin │ │ │ ├── skills.tin │ │ │ ├── special.tin │ │ │ └── status.tin │ │ ├── cmds │ │ │ ├── finger.tin │ │ │ ├── jobquery.tin │ │ │ └── who.tin │ │ ├── login.extra.tin │ │ ├── map │ │ │ ├── __init__.tin │ │ │ ├── area.tin │ │ │ ├── dungeon.tin │ │ │ ├── gmcp.tin │ │ │ ├── helper.tin │ │ │ ├── node.tin │ │ │ ├── room.tin │ │ │ ├── tab.tin │ │ │ └── xiaoyao.tin │ │ └── title.tin │ │ ├── bot │ │ ├── pp-server.tin │ │ └── pp.tin │ │ ├── gmcp.tin │ │ ├── lib │ │ ├── sync.tin │ │ ├── ui │ │ │ ├── chat.tin │ │ │ ├── move.tin │ │ │ └── prompt.extra.tin │ │ └── xtintin │ │ │ └── cmds.extra.tin │ │ ├── quest │ │ └── tang.tin │ │ └── shortcut.tin └── thuxyj │ ├── framework │ └── online.extra.tin │ └── plugins │ ├── basic │ └── login.extra.tin │ └── lib │ └── sync.tin ├── nanorc ├── paotin-start ├── plugins ├── EXAMPLE.tin ├── basic │ └── login.tin ├── lib │ ├── alert.tin │ ├── event.tin │ ├── ga.tin │ ├── gmcp.tin │ ├── option.tin │ ├── speedo.tin │ ├── storage.tin │ ├── tab-completion.tin │ ├── telnet.tin │ ├── ui │ │ ├── __main__.tin │ │ ├── beautify.tin │ │ ├── keyboard.tin │ │ ├── mouse.tin │ │ ├── prompt.tin │ │ ├── tmux.tin │ │ └── walk.tin │ └── xtintin │ │ ├── __init__.tin │ │ ├── algo.tin │ │ ├── bool.tin │ │ ├── buffer.tin │ │ ├── cmds.tin │ │ ├── debug.tin │ │ ├── default.tin │ │ ├── doc.tin │ │ ├── fp.tin │ │ ├── id.tin │ │ ├── list.tin │ │ ├── mslp.tin │ │ ├── number.tin │ │ ├── path.tin │ │ ├── queue.tin │ │ ├── set.tin │ │ ├── slist.tin │ │ ├── string.tin │ │ ├── table.tin │ │ └── time.tin └── silent.tin ├── profile.sh ├── setup └── tmux.conf /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.git 2 | **/*.log 3 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Docker build and publish 2 | 3 | # This workflow uses actions that are not certified by GitHub. 4 | # They are provided by a third-party and are governed by 5 | # separate terms of service, privacy policy, and support 6 | # documentation. 7 | 8 | on: 9 | push: 10 | branches: [ "beta" ] 11 | # Publish semver tags as releases. 12 | tags: [ 'v*.*.*' ] 13 | pull_request: 14 | branches: [ "master" ] 15 | 16 | env: 17 | # Use docker.io for Docker Hub if empty 18 | REGISTRY: docker.io 19 | # github.repository as / 20 | IMAGE_NAME: ${{ github.repository }} 21 | 22 | 23 | jobs: 24 | build: 25 | runs-on: ubuntu-latest 26 | 27 | permissions: 28 | contents: read 29 | packages: write 30 | # This is used to complete the identity challenge 31 | # with sigstore/fulcio when running outside of PRs. 32 | id-token: write 33 | 34 | steps: 35 | - name: Checkout repository 36 | uses: actions/checkout@v3 37 | 38 | # Install the cosign tool except on PR 39 | # https://github.com/sigstore/cosign-installer 40 | - name: Install cosign 41 | if: github.event_name != 'pull_request' 42 | uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 43 | with: 44 | cosign-release: 'v2.1.1' 45 | 46 | # Set up BuildKit Docker container builder to be able to build 47 | # multi-platform images and export cache 48 | # https://github.com/docker/setup-buildx-action 49 | - name: Set up Docker Buildx 50 | uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 51 | 52 | # Login against a Docker registry except on PR 53 | # https://github.com/docker/login-action 54 | - name: Log into registry ${{ env.REGISTRY }} 55 | if: github.event_name != 'pull_request' 56 | uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 57 | with: 58 | registry: ${{ env.REGISTRY }} 59 | username: ${{ github.actor }} 60 | password: ${{ secrets.DOCKER_HUB_TOKEN }} 61 | 62 | # Extract metadata (tags, labels) for Docker 63 | # https://github.com/docker/metadata-action 64 | - name: Extract Docker metadata 65 | id: meta 66 | uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 67 | with: 68 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 69 | 70 | # Build and push Docker image with Buildx (don't push on PR) 71 | # https://github.com/docker/build-push-action 72 | - name: Build and push Docker image 73 | id: build-and-push 74 | uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 75 | with: 76 | context: . 77 | platforms: linux/i386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64 78 | push: ${{ github.event_name != 'pull_request' }} 79 | tags: ${{ steps.meta.outputs.tags }} 80 | labels: ${{ steps.meta.outputs.labels }} 81 | -------------------------------------------------------------------------------- /.github/workflows/windows-publish.yml: -------------------------------------------------------------------------------- 1 | name: Windows build and publish 2 | 3 | # This workflow uses actions that are not certified by GitHub. 4 | # They are provided by a third-party and are governed by 5 | # separate terms of service, privacy policy, and support 6 | # documentation. 7 | 8 | on: 9 | push: 10 | branches: [ "beta" ] 11 | # Publish semver tags as releases. 12 | tags: [ 'v*.*.*' ] 13 | pull_request: 14 | branches: [ "master" ] 15 | 16 | env: 17 | # Use docker.io for Docker Hub if empty 18 | REGISTRY: docker.io 19 | # github.repository as / 20 | IMAGE_NAME: ${{ github.repository }} 21 | 22 | 23 | jobs: 24 | build: 25 | runs-on: windows-2019 26 | 27 | outputs: 28 | name: ${{ steps.exe-name.outputs.EXENAME }} 29 | 30 | defaults: 31 | run: 32 | shell: C:\tools\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}' 33 | 34 | steps: 35 | - name: Set up Cygwin 36 | uses: egor-tensin/setup-cygwin@v4 37 | with: 38 | packages: make gcc-g++ zlib-devel 39 | 40 | - name: Set git to use LF 41 | shell: pwsh 42 | run: | 43 | git config --global core.autocrlf false 44 | git config --global core.eol lf 45 | 46 | - name: Check out code 47 | uses: actions/checkout@v1 48 | with: 49 | submodules: true 50 | 51 | - name: Get current time 52 | id: time1 53 | uses: Kaven-Universe/github-action-current-date-time@v1 54 | with: 55 | format: "YYYYMMDD-HHmmss" 56 | timezone-offset: -480 57 | 58 | - uses: benjlevesque/short-sha@v2.2 59 | id: short-sha 60 | with: 61 | length: 6 62 | 63 | - name: Generate artifact name 64 | id: exe-name 65 | run: | 66 | echo "$GITHUB_OUTPUT" 67 | echo "EXENAME=$EXENAME" >> "$GITHUB_OUTPUT" 68 | env: 69 | EXENAME: paotin-for-windows-setup-${{ steps.time1.outputs.time }}-B${{ github.run_number }}-g${{ steps.short-sha.outputs.sha }}.exe 70 | 71 | - name: Install PCRE 72 | run: | 73 | cd $(cygpath $GITHUB_WORKSPACE) 74 | curl -sL https://sourceforge.net/projects/pcre/files/pcre/8.45/pcre-8.45.tar.bz2/download | tar jxvf - 75 | (cd pcre-8.45 && ./configure --enable-unicode-properties --prefix=$(pwd)/usr LDFLAGS=-static && make && make install) 76 | 77 | - name: Compile 78 | env: 79 | CFLAGS: -I../../pcre-8.45/usr/include 80 | CPPFLAGS: -I../../pcre-8.45/usr/include 81 | LDFLAGS: -static -L../../pcre-8.45/usr/lib 82 | run: | 83 | cd $(cygpath $GITHUB_WORKSPACE) 84 | (cd tintin/src && ./configure && make && strip tt++ || cat config.log) 85 | 86 | - name: Check TinTin++ 87 | run: | 88 | cd $(cygpath $GITHUB_WORKSPACE) 89 | ls -lh tintin/src/tt++ 90 | file tintin/src/tt++ 91 | ldd tintin/src/tt++ 92 | tintin/src/tt++ -V || true 93 | 94 | - name: Packaging 95 | run: | 96 | cd $(cygpath $GITHUB_WORKSPACE) 97 | cp tintin/src/tt++.exe bin/ 98 | cp /bin/cygwin1.dll bin/ 99 | rm -rf .git 100 | rm -rf .github 101 | rm -rf tintin 102 | rm -rf pcre-8.45 103 | 104 | - name: Create installer 105 | uses: joncloud/makensis-action@v4 106 | with: 107 | script-file: install.nsi 108 | arguments: "/V3" 109 | 110 | - name: Rename installer 111 | run: | 112 | cd $(cygpath $GITHUB_WORKSPACE) 113 | mv setup.exe ${{ steps.exe-name.outputs.EXENAME }} 114 | 115 | - name: Create artifact -- PaoTin++ Installer 116 | uses: actions/upload-artifact@v3 117 | with: 118 | name: ${{ steps.exe-name.outputs.EXENAME }} 119 | path: ${{ steps.exe-name.outputs.EXENAME }} 120 | 121 | - name: Create artifact -- WinTin++ 122 | uses: actions/upload-artifact@v3 123 | with: 124 | name: WinTin++ 125 | path: | 126 | ${{ github.workspace }}/bin/tt++.exe 127 | ${{ github.workspace }}/bin/cygwin1.dll 128 | 129 | - name: Release 130 | uses: softprops/action-gh-release@v1 131 | if: startsWith(github.ref, 'refs/tags/') 132 | with: 133 | files: | 134 | ${{ github.workspace }}/tintin/src/tt++.exe 135 | ${{ github.workspace }}/README.md 136 | 137 | publish: 138 | runs-on: ubuntu-latest 139 | needs: build 140 | steps: 141 | - name: Retrieve artifact files 142 | uses: actions/download-artifact@v3 143 | with: 144 | name: ${{ needs.build.outputs.name }} 145 | 146 | - name: Send to file server 147 | uses: appleboy/scp-action@v0.1.4 148 | with: 149 | host: ${{ secrets.FILE_SERVER_HOST }} 150 | port: ${{ secrets.FILE_SERVER_PORT }} 151 | username: ${{ secrets.FILE_SERVER_USER }} 152 | key: ${{ secrets.FILE_SERVER_KEY }} 153 | source: ${{ needs.build.outputs.name }} 154 | target: ${{ secrets.FILE_SERVER_ROOT }}/Windows/ 155 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .bash_history 4 | .bash_profile 5 | .viminfo 6 | .tintin/* 7 | .local 8 | .cache 9 | .lesshst 10 | 11 | bin/tt++ 12 | bin/tt-* 13 | bin/tmux* 14 | !bin/tmux-ui 15 | 16 | log/* 17 | !log/.gitkeep 18 | ids/* 19 | !ids/DEFAULT 20 | !ids/EXAMPLE 21 | 22 | tmux/* 23 | 24 | foo.tin 25 | bar.tin 26 | mwe.tin 27 | bug.tin 28 | test.tin 29 | out 30 | out[1-9] 31 | *.out 32 | *.old 33 | *.bak 34 | *.jpg 35 | *.png 36 | 37 | var/* 38 | var 39 | 40 | data/* 41 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tintin"] 2 | path = tintin 3 | url = https://github.com/mudclient/tintin.git 4 | branch = beta-develop 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # STAGE 1,在临时镜像中编译 tintin 2 | FROM alpine:3.18.3 3 | 4 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories 5 | 6 | # 安装编译器和依赖包 7 | RUN apk update \ 8 | && apk add --no-cache git gcc libc-dev zlib-dev zlib-static pcre-dev make curl 9 | 10 | RUN git clone --depth 1 https://github.com/junegunn/vim-plug.git /vim-plug 11 | RUN git clone --depth 1 https://github.com/dzpao/vim-mbs.git /vim-mbs 12 | RUN git clone --depth 1 https://github.com/morhetz/gruvbox.git /gruvbox 13 | RUN git clone --depth 1 https://github.com/yegappan/mru.git /mru 14 | RUN git clone --depth 1 https://github.com/jlanzarotta/BufExplorer.git /BufExplorer 15 | RUN git clone --depth 1 https://github.com/mhinz/vim-startify.git /vim-startify 16 | 17 | RUN git clone --depth 1 https://github.com/mudclient/tintin.git --branch beta-develop 18 | WORKDIR /tintin/src/ 19 | 20 | ENV PATH=.:/sbin:/bin:/usr/sbin:/usr/bin 21 | RUN ./configure LDFLAGS=-static && make && strip tt++ 22 | 23 | # STAGE 2: 生成最终镜像 24 | FROM alpine:3.18.3 25 | LABEL name="paotin" 26 | LABEL maintainer="dzp " 27 | 28 | ENV LANG=zh_CN.UTF8 \ 29 | TERM=xterm-256color \ 30 | SHELL=/bin/bash \ 31 | HOME=/paotin \ 32 | PATH=/paotin/bin:/usr/sbin:/usr/bin:/sbin:/bin 33 | 34 | WORKDIR /paotin/ 35 | 36 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories 37 | 38 | RUN apk update \ 39 | && apk add --no-cache tmux bash ncurses less neovim nano lf 40 | 41 | # 设置时区为上海 42 | RUN apk add --no-cache tzdata \ 43 | && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ 44 | && echo "Asia/Shanghai" > /etc/timezone \ 45 | && apk del tzdata 46 | 47 | COPY profile.sh /paotin/.bash_profile 48 | COPY tmux.conf /paotin/.tmux.conf 49 | COPY init.tin /paotin/init.tin 50 | 51 | COPY HOW-TO-PLAY.md /paotin/ 52 | COPY bin /paotin/bin/ 53 | COPY docs /paotin/docs/ 54 | COPY etc /paotin/etc/ 55 | COPY framework /paotin/framework/ 56 | COPY plugins /paotin/plugins/ 57 | COPY mud /paotin/mud/ 58 | 59 | COPY ids/EXAMPLE /paotin/ids/ 60 | COPY ids/DEFAULT /paotin/ids/ 61 | COPY ids/pkuxkx /paotin/ids/ 62 | COPY ids/thuxyj /paotin/ids/ 63 | COPY ids/paotin /paotin/ids/ 64 | 65 | COPY init.vim /paotin/.config/nvim/ 66 | COPY nanorc /root/.nanorc 67 | 68 | COPY --from=0 /vim-plug/plug.vim /paotin/.local/share/nvim/site/autoload/plug.vim 69 | COPY --from=0 /gruvbox /paotin/.local/share/nvim/plugged/gruvbox/ 70 | COPY --from=0 /vim-mbs /paotin/.local/share/nvim/plugged/vim-mbs/ 71 | COPY --from=0 /mru /paotin/.local/share/nvim/plugged/mru/ 72 | COPY --from=0 /BufExplorer /paotin/.local/share/nvim/plugged/BufExplorer/ 73 | COPY --from=0 /vim-startify /paotin/.local/share/nvim/plugged/vim-startify/ 74 | 75 | COPY --from=0 /tintin/src/tt++ /paotin/bin/ 76 | 77 | RUN mkdir -p /paotin/log/ 78 | RUN mkdir -p /paotin/tmux/ 79 | 80 | ENTRYPOINT ["/bin/bash", "-i", "/paotin/bin/start-ui"] 81 | -------------------------------------------------------------------------------- /HOW-TO-PLAY.md: -------------------------------------------------------------------------------- 1 | ## 这是哪里? 2 | 3 | 你现在看到的这个画面,是 bash 命令行。没错,你还没有进入游戏,还没有真正启动客户端。 4 | 5 | ## 那么我该如何开始? 6 | 7 | 朋友,听我说,先别急着动手,把这十几行文字看完。有几个重要的命令你需要记一下。 8 | 9 | 你可以输入 `start <游戏名称>` 来进入某个游戏: 10 | - 输入 `start pkuxkx` 可以连接北大侠客行。 11 | - 输入 `start thuxyj` 可以连接清华西游记。 12 | - 输入 `start paotin` 可以进入 PaoTin++ 学习环境,而不用连接任何游戏。 13 | - 输入 `start tintin` 可以进入 TinTin++ 环境。如果你之前没有接触过 TinTin++,请忽略这条。 14 | 15 | ## 怎么自动登录游戏? 16 | 17 | 如果你已经准备好了启动配置文件的话,也可以直接输入 `start <你的ID>` 来启动,例如 `start dzp`。 18 | 如果你还没有准备好启动配置文件,你可以用前面的方法先手动登录,然后 PaoTin++ 会自动为你 19 | 生成启动配置文件,下次你就可以用 `start <你的ID>` 自动登录了。 20 | 21 | 启动配置文件统一存放在 `ids/` 目录下,但如果存在 `var/ids/` 目录,则会在那里。 22 | 23 | ## 几点忠告 24 | 25 | 建议先了解一下本客户端的目录结构,以及主要文件的作用和位置,搞清楚有哪些需要你 26 | 配合修改的地方。请输入 `doc docs/DIRECTORY.md` 来了解客户端的目录结构。 27 | 28 | 任何时候,只要你是在 bash 命令行下,你都可以通过输入 `doc` 命令来再次查看本说明。 29 | 如果你不确定自己是不是在 bash 命令行下,请多输入几次 `#end` 然后回车。 30 | 31 | 交流 QQ 群: 951665549 入群暗号:「PaoTin++ 新用户」。 32 | -------------------------------------------------------------------------------- /Justfile: -------------------------------------------------------------------------------- 1 | # 默认行为,也就是本命令 2 | help: 3 | @echo '输入 just 执行预定义的动作。' 4 | @just --list --unsorted 5 | 6 | # 制作镜像 7 | build-image: 8 | docker image rm mudclient/paotin 9 | docker image prune 10 | docker build -t mudclient/paotin . 11 | 12 | # 制作镜像(beta 版) 13 | build-image-beta: 14 | docker image rm mudclient/paotin:beta 15 | docker image prune 16 | docker build -t mudclient/paotin:beta . 17 | -------------------------------------------------------------------------------- /bin/doc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DOC_FILE=$1 4 | 5 | if [ "x$DOC_FILE" = "x" ]; then 6 | DOC_FILE=HOW-TO-PLAY 7 | fi 8 | 9 | DOC_FILE=${DOC_FILE%%.md}.md 10 | 11 | cat $DOC_FILE \ 12 | | sed '/^```/,/^```/s/^//' \ 13 | | sed '/^\[32m```$/d' \ 14 | | sed 's/^\(##* .*\)$/\1/' \ 15 | | sed 's/`//' | sed 's/`//' \ 16 | | sed 's/`//' | sed 's/`//' \ 17 | | sed 's/$//' 18 | -------------------------------------------------------------------------------- /bin/mtail: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #nop vim: set filetype=tt:; 4 | #nop { 5 | 6 | ID=$1 7 | LOGS="${*:2}" 8 | 9 | if [ "x$LOGS" == "x" ]; then 10 | LOGS="chat qq jh helpme fullsk quest job tell" 11 | fi 12 | 13 | if [ "x$ID" == "x" ]; then 14 | echo -e '用法: \x1b[1;32;92mmtail \x1b[1;33;93m\x1b[0m [ ...]' 15 | echo '日志名称不用加路径和 .log 后缀,只要文件名就好。' 16 | echo "可以同时显示多个日志,默认显示日志: $LOGS" 17 | echo '别忘了写 ID,加油!' 18 | exit 19 | fi 20 | 21 | if [[ ! -d "var/log/$ID" && ! -d "log/$ID/" ]]; then 22 | echo 没找到 $ID 的游戏日志。请先打开游戏再执行本操作。; 23 | exit 24 | fi 25 | 26 | exec tt++ -G -t MLOG-$ID bin/mtail $ID $LOGS 27 | exit 28 | 29 | }; 30 | 31 | #event {PROGRAM START} { 32 | #if { "%0" == "" } { 33 | #line quiet #end {\}; 34 | }; 35 | 36 | disable-all-keys; 37 | less-mode; 38 | 39 | #alias {okLog} {#0}; 40 | #alias {warnLog} {#0}; 41 | #alias {prompt.Set} {#0}; 42 | #alias {prompt.refresh} {#0}; 43 | #read plugins/lib/ui/beautify.tin; 44 | #local _ {@lib_ui_beautify.Init{}}; 45 | monitor-log %0; 46 | }; 47 | 48 | #alias {disable-all-keys} { 49 | #local ch {}; 50 | #parse {abcdefghijklmnopqrstuvwxyz} {ch} { 51 | #macro {\c$ch} {#0}; 52 | #macro {$ch} {#0}; 53 | }; 54 | #parse {ABCDEFGHIJKLMNOPQRSTUVWXYZ} {ch} { 55 | #macro {$ch} {#0}; 56 | }; 57 | #parse {`1234567890-=~!@#$%^&*()_+} {ch} { 58 | #macro {$ch} {#0}; 59 | }; 60 | #parse {,./<>?;':"[]\\|} {ch} { 61 | #macro {$ch} {#0}; 62 | }; 63 | #macro {\x7B} {#0}; 64 | #macro {\x7D} {#0}; 65 | #macro { } {#0}; 66 | 67 | #macro {\cn} {switch-log +}; 68 | #macro {\cp} {switch-log -}; 69 | 70 | #var ctrl-c-pressed {false}; 71 | #macro {\cc} { 72 | #if { "$ctrl-c-pressed" == "false" } { 73 | #echo {如果你想要退出,请再按一次 Ctrl+C。}; 74 | #var ctrl-c-pressed {true}; 75 | #delay cancel { 76 | #echo {等待超时,恢复正常。}; 77 | #var ctrl-c-pressed {false}; 78 | } {1.000}; 79 | }; 80 | #else { 81 | #screen set title {NOLOG}; 82 | #line quiet #end {\}; 83 | }; 84 | }; 85 | }; 86 | 87 | #alias {less-mode} { 88 | #config {mouse} on; 89 | #event {SCROLLED MOUSE WHEEL DOWN} {#buffer down 10}; 90 | #event {SCROLLED MOUSE WHEEL UP} {#buffer up 10}; 91 | #event {SCROLLED CTRL MOUSE WHEEL DOWN} {#buffer down 1}; 92 | #event {SCROLLED CTRL MOUSE WHEEL UP} {#buffer up 1}; 93 | #macro {g} {#buffer home}; 94 | #macro {G} {#buffer end}; 95 | #macro {j} {#buffer down 1}; 96 | #macro {k} {#buffer up 1}; 97 | #macro {\cf} {#buffer down 20}; 98 | #macro {\cb} {#buffer up 20}; 99 | #macro {\ch} {hide-date}; 100 | }; 101 | 102 | #var hide-date {1}; 103 | #alias {hide-date} { 104 | #switch {$hide-date} { 105 | #case {0} {#var hide-date 1; #echo {<160>日志显示模式已切换至 HH:MM:SS<299>}}; 106 | #case {1} {#var hide-date 2; #echo {<160>日志显示模式已切换至 HH:MM<299>}}; 107 | #case {2} {#var hide-date 3; #echo {<160>日志显示模式已切换至不显示时间戳<299>}}; 108 | #case {3} {#var hide-date 0; #echo {<160>日志显示模式已切换至 YYYY-mm-dd HH:MM:SS<299>}}; 109 | }; 110 | }; 111 | 112 | #alias {switch-log} { 113 | #local dir {%1}; 114 | #if { "$dir" == "" } { 115 | #local dir {+}; 116 | }; 117 | #session $dir; 118 | #buffer end; 119 | #script logname {echo $LOGNAME > tmux/$ID/log-name}; 120 | #screen set title {MLOG-${ID}-${LOGNAME}}; 121 | }; 122 | 123 | #alias {monitor-log%+1..s%+1..S{?:|\s+(.*)}$} { 124 | #local id {%2}; 125 | #local logs {%3}; 126 | 127 | #local path {}; 128 | #script path {test -d var/log/$id/ && echo var/log/$id || echo log/$id}; 129 | #local path {$path[1]}; 130 | #list logs create $logs; 131 | #local log {}; 132 | #foreach {$logs[]} {log} { 133 | #local file {$path/${log}.log}; 134 | #script {tmp} {touch $file}; 135 | #line quiet #run {log-$log} {tail -n 1000 -f $path/${log}.log}; 136 | #var LOGNAME {$log}; 137 | #var ID {$id}; 138 | }; 139 | 140 | switch-log; 141 | }; 142 | 143 | #action {~^{\d\d\d\d-\d\d-\d\d }{\d\d:\d\d}{:\d\d}{ }%*$} { 144 | #switch {$hide-date} { 145 | #case {0} {#echo {%s} {@Beautify{%0}}}; 146 | #case {1} {#echo {%s} {@Beautify{%2%3%4%5}}}; 147 | #case {2} {#echo {%s} {@Beautify{%2%4%5}}}; 148 | #case {3} {#echo {%s} {@Beautify{%5}}}; 149 | }; 150 | #line gag; 151 | }; 152 | 153 | #event {SCREEN RESIZE} {#buffer end}; 154 | -------------------------------------------------------------------------------- /bin/plain: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | FILE=$1 4 | 5 | if [ "x$FILE" == "x-h" ]; then 6 | echo -e '用法: \x1b[1;32;92mplain \x1b[1;33;93m<原始文件名>\x1b[0m'; 7 | echo 可以将指定的文件中的颜色代码全部过滤掉。; 8 | echo 结果打印到标准输出。; 9 | echo; 10 | echo 如果省略文件名,则从标准输入读取内容。; 11 | exit 12 | fi 13 | 14 | sed 's/\x1b\[[0-9;]*m//g' $FILE 15 | -------------------------------------------------------------------------------- /bin/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TT= 4 | ID=$1 5 | START_FILE=$1 6 | 7 | ECHO="$(which echo) -e" 8 | 9 | if [ "$(uname -s)" = "Darwin" ]; then 10 | ECHO=echo 11 | fi 12 | 13 | if [ "x$START_FILE" = "x" ]; then 14 | $ECHO "Usage: $0 " 15 | exit 16 | fi 17 | 18 | if [ "x$START_FILE" = "xtintin" ]; then 19 | tt++ -G -r init.tin 20 | clear; doc 21 | exit 22 | fi 23 | 24 | NEWBIE=false 25 | if [ "x$START_FILE" = "xpkuxkx" -o "x$START_FILE" = "xthuxyj" ]; then 26 | NEWBIE=true 27 | fi 28 | 29 | if [ ! -z $(which tt-beta 2>/dev/null) ]; then 30 | TT=tt-beta 31 | elif [ ! -z $(which tt++ 2>/dev/null) ]; then 32 | TT=tt++ 33 | else 34 | $ECHO "\x1b[1;31m软件安装错误,没有找到可用的 tintin,请重新安装。\x1b[0m" 35 | exit 36 | fi 37 | 38 | if [ -f var/ids/$START_FILE ]; then 39 | START_FILE=var/ids/$START_FILE 40 | elif [ -f ids/$START_FILE ]; then 41 | START_FILE=ids/$START_FILE 42 | else 43 | $ECHO "\x1b[1;31m不存在启动配置文件 \x1b[1;33m$START_FILE\x1b[1;31m。\x1b[0m" 44 | exit 45 | fi 46 | 47 | $ECHO "\x1b[1;32m启动 tintin...\x1b[0m" 48 | $ECHO "\x1b[1;32m$TT $START_FILE\x1b[0m" 49 | 50 | mkdir -p tmux/$ID 51 | 52 | log() { 53 | MSG=$1 54 | TIME=$(date +'%Y-%m-%d %H:%M:%S') 55 | echo $TIME $START_FILE $MSG >> log/tintin.log 56 | } 57 | 58 | tintin() { 59 | log "TinTin++ 启动。" 60 | $TT -G -t GAME-$ID $START_FILE 61 | } 62 | 63 | tintin 64 | CODE=$? 65 | INTERVAL=1 66 | MAX_INTERVAL=1800 67 | while [ "$CODE" -ne "0" ]; do 68 | if [ "$INTERVAL" -gt "$MAX_INTERVAL" ]; then 69 | INTERVAL=$MAX_INTERVAL 70 | fi 71 | log "TinTin++ 非正常退出,exit code = $CODE,等待 $INTERVAL 秒后重新启动。" 72 | sleep $INTERVAL 73 | INTERVAL=$(expr $INTERVAL '*' 2) 74 | tintin 75 | CODE=$? 76 | done 77 | 78 | log "TinTin++ 正常退出,exit code = $CODE" 79 | 80 | if [ "$NEWBIE" = "true" ]; then 81 | clear; doc 82 | fi 83 | -------------------------------------------------------------------------------- /bin/start-ui: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | MODE=$1 4 | 5 | if [ "x$MODE" == "xdaemon" ]; then 6 | while true; do bash; done 7 | exit 8 | fi 9 | 10 | mkdir -p $HOME/tmux 11 | 12 | if [ "x$TMUXCMD" = "x" ]; then 13 | export TMUXCMD="tmux -S $HOME/tmux/sock" 14 | fi 15 | 16 | SESSION=MUD 17 | 18 | function create-ui() { 19 | echo 正在生成 UI... 20 | 21 | HEIGHT=$(tput lines || ( [[ "x$LINES" != "x" ]] && echo $LINES ) || echo 30) 22 | HEIGHT=$(expr $HEIGHT - 1 - 1 - 1 - 6) 23 | 24 | # 创建一个标准会话 25 | $TMUXCMD new-session -d -s $SESSION 26 | $TMUXCMD new-window -t $SESSION 27 | $TMUXCMD send-keys -t $SESSION "sleep 1; clear; doc docs/tmux.md" 28 | $TMUXCMD send-keys -t $SESSION Enter 29 | $TMUXCMD select-window -t $SESSION:0 30 | $TMUXCMD send-keys -t $SESSION "sleep 0.5; clear; doc HOW-TO-PLAY.md" 31 | $TMUXCMD send-keys -t $SESSION Enter 32 | 33 | echo UI 已创建。 34 | } 35 | 36 | # 停顿一下,等待终端状态同步。 37 | echo 正在打开终端... 38 | sleep 0.5; 39 | 40 | # 如果是首次启动,则先创建 UI。否则优先连接到已有的会话,继续之前的状态。 41 | if ! $TMUXCMD has-session 2>/dev/null; then 42 | create-ui; 43 | fi 44 | 45 | echo 正在连接 UI... 46 | 47 | exec $TMUXCMD attach-session -d -t $SESSION 48 | -------------------------------------------------------------------------------- /bin/tmux-ui: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # 这是一堆乱七八糟的和 tmux UI 元素相关的小功能的集合。 5 | # 通过 ELEMENT 来区分想要的是哪个元素,目前支持的有 LOGO/WIN/PANE 三大类 6 | # LOGO: tmux 右下角的一个 LOGO 显示,计划将版本号也放进去 7 | # WIN: tmux 状态栏中,窗口标题会显示角色 ID 8 | # PANE: 会在 tmux pane 上显示一些有用的信息,目前支持两种,用第二个参数 ARG 来控制 9 | # ARG=GAME-id,表示这是游戏主面板,用来显示一些角色数据,约定存放在 tmux/ID/ 目录下 10 | # ARG=MLOG-id,表示这是游戏日志监视器,会在 pane 边框线上显示日志名称 11 | # 12 | # 需要配合 tmux.conf 使用,将需要的参数特别是 ID 要传过来。 13 | # 14 | 15 | ELEMENT=$1 16 | ARG=$2 17 | ARG2=$3 18 | ARG3=$4 19 | ID= 20 | LOGNAME= 21 | 22 | if [[ "x$ARG" == xGAME-* ]]; then 23 | ID=${ARG:5} 24 | mkdir -p tmux/$ID 25 | ARG=GAME 26 | fi 27 | 28 | if [[ "x$ARG" =~ xMLOG-(.*)-(.*) ]]; then 29 | ID=${BASH_REMATCH[1]} 30 | LOGNAME=${BASH_REMATCH[2]} 31 | mkdir -p tmux/$ID 32 | ARG=MLOG 33 | fi 34 | 35 | # 每隔十秒钟轮换显示英文和中文 LOGO 36 | if [ "x$ELEMENT" == "xLOGO" ]; then 37 | echo -e 'PaoTin++\n庖丁加加' | head -$(expr $(date '+%s') '/' 10 '%' 2 '+' 1) | tail -1 38 | exit 39 | fi 40 | 41 | if [ "x$ELEMENT" == "xWIN" ]; then 42 | if [ "x$ID" != "x" ]; then 43 | echo $ID 44 | else 45 | echo '#W' 46 | fi 47 | exit 48 | fi 49 | 50 | if [ "x$ELEMENT" == "xPANE" ]; then 51 | if [ "x$ARG" == "xMLOG" ]; then 52 | echo -n " 正在查看 #[bg=blue fg=brightyellow bold]$LOGNAME#[default] 日志" 53 | if [ "x$ARG2" == "x1" ]; then 54 | echo -n ",Ctrl+N/Ctrl+P 切换,Ctrl+CC 退出 " 55 | else 56 | echo -n " " 57 | fi 58 | elif [ "x$ARG" == "xGAME" ]; then 59 | echo -n "$(cat tmux/$ID/game-border || echo " 尚未找到 $ID 的游戏数据,请检查机器人版本 ")" 60 | else 61 | WIDTH=${ARG3-0} 62 | if [ $WIDTH -gt 100 ]; then 63 | echo -n ' 终端: #{client_termname} 当前目录: #{pane_current_path} #[align=right] 尺寸: #{pane_width}x#{pane_height} ' 64 | elif [ $WIDTH -gt 80 ]; then 65 | echo -n ' #{client_termname} #{pane_current_path} #[align=right] #{pane_width}x#{pane_height} ' 66 | elif [ $WIDTH -gt 40 ]; then 67 | echo -n ' #{client_termname} #[align=right] #{pane_width}x#{pane_height} ' 68 | elif [ $WIDTH -gt 20 ]; then 69 | echo -n '#[align=right] #{pane_width}x#{pane_height} ' 70 | else 71 | echo -n '' 72 | fi 73 | fi 74 | fi 75 | -------------------------------------------------------------------------------- /data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudclient/paotin/12711b19c3ac182a44a0fd88dfe95d6b7fe33ca3/data/.gitkeep -------------------------------------------------------------------------------- /docs/DIRECTORY.md: -------------------------------------------------------------------------------- 1 | ## 目录结构和文件说明 2 | 3 | ``` 4 | > tree -L 2 5 | . 6 | ├── HOW-TO-PLAY.md -- 简要说明,新手指引文件 7 | ├── bin 8 | │   ├── start-ui -- 启动游戏环境(第一步,如果是 Docker 环境则已执行过了) 9 | │   ├── start -- 启动游戏账号(第二步) 10 | │   ├── doc -- markdown 文档查看工具。如果显示异常,请用 cat 代替。 11 | │   ├── mtail -- 日志查看工具 12 | │   └── tt++ -- tintin++ 主程序 13 | ├── docs 14 | │   ├── tmux.md -- PaoTin++ 快捷键说明 15 | │   └── DIRECTORY.md -- 本文件 16 | ├── etc -- 客户端配置文件目录(跨 ID 共享) 17 | ├── data -- 客户端数据文件目录(跨 ID 共享) 18 | ├── framework -- 主框架程序,不建议修改 19 | ├── ids -- 启动配置文件存放目录。建议以 ID 命名,不同 ID 不同文件 20 | │   ├── DEFAULT -- 所有 ID 的默认配置,可以在启动配置文件里定制 21 | │   ├── pkuxkx -- 连接北大侠客行游戏服务器 22 | │   ├── thuxyj -- 连接清华西游记游戏服务器 23 | │   ├── paotin -- 启动 PaoTin++ 学习环境 24 | │   └── EXAMPLE -- 一个启动配置文件的例子 25 | ├── log -- 日志存放目录 26 | ├── plugins -- 插件存放目录 27 | │   ├── lib -- 基础插件 28 | │   ├── basic -- 基本插件 29 | │   ├── job -- 任务插件目录 30 | │   ├── bot -- 常用机器人目录 31 | │   ├── quest -- 一些小任务或者解密文件 32 | │   └── shortcut.tin -- 其它插件 33 | └── var -- Docker 环境下用作本地存储用的挂载目录 34 | ├── ids -- 本地启动配置文件 35 | ├── etc -- 本地配置 36 | ├── log -- 本地日志 37 | └── plugins -- 本地插件目录,其内部结构和上面的 plugins 相同 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/tmux.md: -------------------------------------------------------------------------------- 1 | ## 什么是 tmux? 2 | 3 | tmux 是一个先进的终端复用器,非常有用。 4 | PaoTin++ 用它来管理 UI,切分屏幕,创建多窗口画面,同时挂机多个 ID。 5 | 6 | 简单入门建议看这篇博文: https://www.ruanyifeng.com/blog/2019/10/tmux.html 7 | 8 | 别担心,tmux 虽然非常有用,但学习起来很容易,只需要记住几个快捷键就可以正常使用了。 9 | 10 | ## 前缀键 11 | 12 | 了解 tmux 的朋友可能知道什么是前缀键,PaoTin++ 的 tmux 前缀键已经被修改为 ``。 13 | 如果你不知道什么是前缀键,只需要记住,下面所有的快捷键都是先按 ``,然后松开, 14 | 继续按后面的字母就可以了。 15 | 16 | ## 快捷键 17 | 18 | PaoTin++ 常用的快捷键有: 19 | 20 | * ` d` 退出界面,但并不退出游戏。 21 | * ` -` 横着划一道,将屏幕分成上下两部分。继续按可以继续分。 22 | * ` |` 竖着划一道,将屏幕分成左右两部分。继续按可以继续分。 23 | * ` - |` 先横着划一道,再竖着划一道,形成一个「品」字形结构。 24 | * ` | -` 先竖着划一道,再横着划一道,形成一个「唱」字形结构。 25 | * ` c` 打开一个新窗口。 26 | * ` 0` 回到 0 号窗口。` 1` 回到 1 号窗口。十个数字都可以用。 27 | 28 | 上面的窗口和分屏都可以用鼠标点击选择。 29 | 30 | ## 进一步学习 31 | 32 | * tao-of-tmux: https://tao-of-tmux.readthedocs.io/zh_CN/latest/ 33 | -------------------------------------------------------------------------------- /etc/ui-settings.tin: -------------------------------------------------------------------------------- 1 | #var tmux-update-events[DEFAULT] {char/score}; 2 | #var tmux-pane-border-format[DEFAULT] { 3 | {left} {ID: [<188>\$char[档案][账号]<099>]} 4 | {centre} {大名: [\$char[档案][大名]]} 5 | {right} {头衔: [\$char[档案][头衔]]} 6 | }; 7 | 8 | #nop {Top} {TopSepBar} {MidSepBar} {Bot} {BotSepBar}; 9 | #list prompt-fields create { 10 | { {place}{BotSepBar} {label}{<119>警报} {name}{alert} } 11 | { {place}{BotSepBar} {label}{屏幕美化} {name}{beautify} {cooldown}{600} {visibility}{HideCool} } 12 | { {place}{BotSepBar} {label}{状态栏更新} {name}{disable} {visibility}{HideLabel} } 13 | { {place}{BotSepBar} {label}{随手记} {name}{note} {color}{<134>}} 14 | }; 15 | 16 | VAR {信息栏的配色主题} prompt-theme { 17 | {Disable}{} 18 | {BusyColor}{<239>} 19 | {BattleColor}{<119>} 20 | {BattleBusyColor}{<599><119>} 21 | {TopSepBar}{<229>} 22 | {MidSepBar}{<229>} 23 | {BotSepBar}{<229>} 24 | {HotLabel}{<199>} 25 | {CoolLabel}{<109>} 26 | {Value}{<299>} 27 | }; 28 | 29 | VAR {自定义图标} prompt-icon { 30 | {DisableRefresh}{🚫} 31 | }; 32 | 33 | VAR {命令提示符,默认为空} prompt-prompt {}; 34 | 35 | #nop 热键绑定; 36 | #list global-key-bindings create { 37 | {{key}{\cd} {meaning}{命令行智能切换} {action}{cli.SmartToggle}} 38 | {{key}{\co\cd} {meaning}{禁止/允许命令和触发} {action}{xtt.ToggleDisableCommands}} 39 | {{key}{\cos} {meaning}{禁止/允许状态栏更新} {action}{prompt.ToggleSwitch}} 40 | {{key}{\cob} {meaning}{禁止/允许界面宽字符美化} {action}{beautify.ToggleSwitch}} 41 | {{key}{\col} {meaning}{查看选项列表} {action}{option.List}} 42 | {{key}{\cod} {meaning}{开启/关闭调试模式} {action}{option.Toggle DebugLog}} 43 | {{key}{\coD} {meaning}{开启/关闭调试日志} {action}{option.Toggle DebugLogEcho}} 44 | {{key}{\coe} {meaning}{开启/关闭命令发送回显} {action}{option.Toggle EchoCommand}} 45 | }; 46 | 47 | VAR {HP摘要刷新时机,三选一:{总是|从不|自动}} char.HPSummarize.Echo {自动}; 48 | 49 | VAR {HP摘要配色表} char.HPSummarize.Theme { 50 | {色卡} {<218>;15;<118>;30;<238>;60;<138>;90;<128>;101;<168>} 51 | {前景} { 52 | {平常} {<168>} 53 | {战斗} {<118>} 54 | {战安} {<128>} 55 | {战危} {<118>} 56 | {战忙} {<138>} 57 | {危急} {<158>} 58 | {正忙} {<138>} 59 | } 60 | {背景} { 61 | {平常} {<099>} 62 | {战斗} {} 63 | {战安} {} 64 | {战危} {} 65 | {战忙} {} 66 | {危急} {} 67 | {正忙} {} 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /framework/class.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:class 功能封装 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #list xtt-class-stack create {}; 14 | 15 | #alias {class.name} { 16 | #local currentClassName {${xtt-class-stack[-1]}}; 17 | }; 18 | 19 | #alias {class.open} { 20 | #local className {%1}; 21 | 22 | #list {xtt-class-stack} {add} {$className}; 23 | #class {$className} open; 24 | }; 25 | 26 | #alias {class.close} { 27 | #local className {%1}; 28 | #class {$className} close; 29 | 30 | #list {xtt-class-stack} {delete} {-1}; 31 | #local preClassName {${xtt-class-stack[-1]}}; 32 | #if { "$preClassName" != "" } { 33 | #class {$preClassName} open; 34 | }; 35 | }; 36 | 37 | #alias {class.kill} { 38 | #local className {%1}; 39 | #class {$className} kill; 40 | }; 41 | 42 | #alias {class.read} { 43 | #local className {%1}; 44 | #local filePath {%2}; 45 | 46 | class.open {$className}; 47 | class.do {$className} {load-file $filePath}; 48 | class.close {$className}; 49 | }; 50 | 51 | #alias {class.do} { 52 | #local className {%1}; 53 | #local code {%2}; 54 | 55 | #class {$className} {assign} {$code}; 56 | }; 57 | 58 | #alias {class.enable} { 59 | #local className {%1}; 60 | #class {$className} {load}; 61 | }; 62 | 63 | #alias {class.disable} { 64 | #local className {%1}; 65 | #class {$className} {save}; 66 | #class {$className} {clear}; 67 | }; 68 | -------------------------------------------------------------------------------- /framework/log.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:日志模块 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #var gLog[buffer] {buffer.log}; 14 | #var gLog[socket] {socket.log}; 15 | 16 | #var gLog[info] {info.log}; 17 | #var gLog[ok] {ok.log}; 18 | #var gLog[warn] {warn.log}; 19 | #var gLog[error] {error.log}; 20 | #var gLog[debug] {debug.log}; 21 | 22 | #var gLog[PATH] {log}; 23 | 24 | #var gLog[initialled] {false}; 25 | 26 | #func {InitLog} { 27 | #local path {%1}; 28 | 29 | #if { @existsDir{var} } { 30 | #var gLog[PATH] {var/log}; 31 | }; 32 | #else { 33 | #var gLog[PATH] {log}; 34 | }; 35 | 36 | #if { !@mkdir{$gLog[PATH]/$path} } { 37 | #return 0; 38 | }; 39 | 40 | #var gLog[PATH] {$gLog[PATH]/$path}; 41 | #var gLog[initialled] {true}; 42 | 43 | load-lib option; 44 | option.Define {DebugLog} {Bool} {是否开启调试日志} {false}; 45 | option.Define {DebugLogEcho} {Bool} {是否显示调试日志} {false}; 46 | option.Define {DebugTags} {Slist} {记录调试日志的 tag 列表} {}; 47 | option.Define {EchoCommand} {Bool} {是否回显发送的命令} {false}; 48 | 49 | #return 1; 50 | }; 51 | 52 | #alias {mudLog} {log.write {$gLog[socket]} {%0}}; 53 | 54 | #alias {okLog} {log.write {$gLog[ok]} {<129>%0} {ECHO}}; 55 | #alias {warnLog} {log.write {$gLog[warn]} {<139>%0} {ECHO}}; 56 | #alias {errLog} {log.write {$gLog[error]} {<119>%0} {ECHO}}; 57 | #alias {infoLog} {log.write {$gLog[info]} {%0} {ECHO}}; 58 | 59 | #alias {dbgLog} { 60 | #local tag {%1}; 61 | 62 | #if { @option.IsDisable{DebugLog} } { 63 | #return; 64 | }; 65 | 66 | #local echo {}; 67 | #if { @option.IsEnable{DebugLogEcho} } { 68 | #local echo {ECHO}; 69 | }; 70 | 71 | #if { @slist.Contains{{@option.Get{DebugTags}};$tag} } { 72 | log.write {$gLog[debug]} {<850>%0<999>} {$echo}; 73 | }; 74 | }; 75 | 76 | #alias {noLog} {#0}; 77 | 78 | #alias {{[a-z]{1,10}}Log %*} { 79 | #local logName {}; 80 | #format logName {%l} {%1}; 81 | log.write {${logName}.log} {%2}; 82 | }; 83 | 84 | #alias {log.write} { 85 | #local file {%1}; 86 | #local text {%2}; 87 | #local echo {%3}; 88 | 89 | #if { "$gLog[initialled]" != "true" } {#return}; 90 | 91 | #line logmode stamp #line log {$gLog[PATH]/$file} {$text<099>}; 92 | 93 | #if { "$echo" == "ECHO" } { 94 | #echo {%s} {$text<099>}; 95 | }; 96 | }; 97 | 98 | #event {RECEIVED INPUT} { 99 | #if { "$gLog[initialled]" != "true" } {#return}; 100 | 101 | #local needEcho {false}; 102 | 103 | #if @option.IsDisable{EchoCommand} { 104 | #local needEcho {true}; 105 | }; 106 | #else { 107 | #line sub escape #var tmp {%0}; 108 | #local cmds {}; 109 | #list cmds create {$tmp}; 110 | #if { &cmds[] > 1 } { 111 | #local needEcho {true}; 112 | }; 113 | }; 114 | 115 | #if { "$needEcho" == "true" } { 116 | #local input {%0}; 117 | #replace input {<} {\<}; 118 | #echo {<029>%t INPUT: <429>%s<099>} {%Y-%m-%d %H:%M:%S} {$input}; 119 | }; 120 | }; 121 | 122 | #alias {log.Open} { 123 | #if { "$gLog[initialled]" != "true" } {#return}; 124 | #config {LOG} {RAW}; 125 | #config {LOG LEVEL} {HIGH}; 126 | #log timestamp {%Y-%m-%d %H:%M:%S }; 127 | #log append {$gLog[PATH]/$gLog[buffer]}; 128 | #event {RECEIVED LINE} {mudLog %%0}; 129 | #event {SEND OUTPUT} {log.sendLog %%0}; 130 | }; 131 | 132 | #alias {log.sendLog} { 133 | #local text {%0}; 134 | #replace text {<} {\<}; 135 | #format text {<029>SEND: <429>%p<099>} {$text}; 136 | mudLog $text; 137 | #if @option.IsEnable{EchoCommand} { 138 | #echo {<029>%t %s} {%Y-%m-%d %H:%M:%S} {$text}; 139 | }; 140 | }; 141 | -------------------------------------------------------------------------------- /framework/main.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:框架主程序 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #class main open; 14 | 15 | #kill all; 16 | 17 | #event {PROGRAM START} { 18 | #if { "$user[manual]" == "true" } { 19 | #var user[id] {}; 20 | #var user[name] {}; 21 | #var user[passwd] {}; 22 | #screen set title {}; 23 | }; 24 | #else { 25 | #if { "$user[id]" != "{[A-Za-z0-9]+}" } { 26 | #echo {<119>请参考使用文档指定用户正确的 user id。<299>}; 27 | #return; 28 | }; 29 | 30 | #local path {$user[id]}; 31 | #if { "$session[log_path]" != "" } { 32 | #local path {$session[log_path]}; 33 | }; 34 | 35 | #if { !@InitLog{$path} } { 36 | #echo {<119>创建日志目录 $gLog[PATH]/$path 时遇到错误。<299>}; 37 | #echo {<139>请检查你的安装环境,或者参考使用手册重新安装本软件。<299>}; 38 | #return; 39 | }; 40 | }; 41 | 42 | load-module basic/login; 43 | auto-login; 44 | }; 45 | 46 | #event {PROGRAM TERMINATION} { 47 | #screen set title {NONE}; 48 | }; 49 | 50 | #alias {auto-login} { 51 | login {$session} {$user} { 52 | load-file {framework/online.tin}; 53 | }; 54 | }; 55 | 56 | #event {SESSION CREATED} { 57 | #nop #gts 中残留的 #tick 会在 mud 会话被创建时继承并错误地激活,从而影响游戏过程,特别是登录。; 58 | #kill ticker; 59 | #if { "%0" !== "$session[name]" } { 60 | #return; 61 | }; 62 | 63 | #%0 { 64 | log.Open; 65 | load-module lib/event; 66 | event.Define {user-online} {无参} {framework} {已经登录到服务器,所有命令都可以正常使用了。}; 67 | load-module lib/ui; 68 | #if { "$user[manual]" != "true" } { 69 | #delay online.WatchDog {#zap} {$session[reconnect]}; 70 | }; 71 | }; 72 | }; 73 | 74 | #event {SESSION TIMED OUT} { 75 | #echo {%s} {<119>连接服务器超时,稍后自动重试。<299>}; 76 | #gts #delay 3 auto-login; 77 | }; 78 | 79 | #event {SESSION DISCONNECTED} { 80 | #if { "%0" == "$session[name]" } { 81 | #local reconnect {$session[reconnect]}; 82 | #if { "$session[remote_maint]" == "true" } { 83 | #local reconnect {$session[reconnect_slow]}; 84 | #var session[remote_maint] {false}; 85 | }; 86 | 87 | #if { "$reconnect" == "{|0}" } { 88 | #local reconnect 12; 89 | }; 90 | 91 | #nop 断开连接后再次重连不要太频繁,以免服务器不高兴。; 92 | #echo {%s} {<119>连接已被服务器断开,$reconnect 秒后自动重连。<299>}; 93 | #gts #delay {$reconnect} { 94 | #echo {%s} {<129>自动重连。<299>}; 95 | auto-login; 96 | }; 97 | }; 98 | }; 99 | 100 | #func {existsFile} { 101 | #local file {%0}; 102 | #var tmpSize {-1}; 103 | #line quiet #scan file {$file} {#var tmpSize &2}; 104 | #local tmp {$tmpSize}; 105 | #unvar tmpSize; 106 | #if { $tmp > -1 } { 107 | #return 1; 108 | }; 109 | #else { 110 | #return 0; 111 | }; 112 | }; 113 | 114 | #func {existsDir} { 115 | #local dir {%0}; 116 | 117 | #class framework.existsDir.catch open; 118 | 119 | #var existsDir.ok 1; 120 | #event {SYSTEM ERROR} {#var existsDir.ok 0}; 121 | 122 | #local output {}; 123 | #line quiet #scan dir {$dir} output; 124 | 125 | #class framework.existsDir.catch close; 126 | 127 | #local ok {$existsDir.ok}; 128 | #class framework.existsDir.catch kill; 129 | 130 | #if { $ok } { 131 | #return 1; 132 | }; 133 | #else { 134 | #return 0; 135 | }; 136 | }; 137 | 138 | #nop 优先加载 var/$file(玩家自定义文件),其次加载 mud/$file(MUDLIB 相关文件); 139 | #alias {load-file} { 140 | #local file {%1}; 141 | 142 | #if { @isEmpty{$file} } { 143 | xtt.Usage load-file; 144 | #return; 145 | }; 146 | 147 | #if { @existsFile{var/$file} } { 148 | #read var/$file; 149 | #return; 150 | }; 151 | 152 | #if { "$gCurrentMUDLIB" != "" } { 153 | #if { @existsFile{mud/$gCurrentMUDLIB/$file} } { 154 | #read mud/$gCurrentMUDLIB/$file; 155 | #return; 156 | }; 157 | }; 158 | 159 | #read $file; 160 | 161 | #replace file {.tin$} {.extra.tin}; 162 | 163 | #if { @existsFile{mud/$gCurrentMUDLIB/$file} } { 164 | #read mud/$gCurrentMUDLIB/$file; 165 | }; 166 | 167 | #if { @existsFile{var/$file} } { 168 | #read var/$file; 169 | }; 170 | }; 171 | 172 | #alias {init} { 173 | #nop 调整 tintin 环境; 174 | load-file framework/settings.tin; 175 | #nop 框架依赖的基本函数; 176 | load-file framework/utils.tin; 177 | #nop 日志支持; 178 | load-file framework/log.tin; 179 | #nop 为 TinTin 赋能,实现模块加载器; 180 | load-file framework/module-loader.tin; 181 | #nop 支持不同的 MUD 服务器环境; 182 | load-file framework/multi-mud.tin; 183 | 184 | #nop 为 TinTin 赋能,自行实现的扩展语法和实用函数集。; 185 | load-lib xtintin; 186 | 187 | #nop 提供 TinTin++ 命令行自动补全功能。; 188 | load-lib tab-completion; 189 | 190 | #nop 默认的用户环境配置; 191 | load-file ids/DEFAULT; 192 | }; 193 | 194 | #var session {}; 195 | #var user {}; 196 | #var char {}; 197 | #var info {}; 198 | 199 | init; 200 | 201 | #class main close; 202 | -------------------------------------------------------------------------------- /framework/multi-mud.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:多 MUD 支持 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #class main open; 14 | 15 | #var gMUDLIB { 16 | {pkuxkx} {北大侠客行} 17 | {thuxyj} {清华西游记} 18 | }; 19 | 20 | #var gCurrentMUDLIB {}; 21 | 22 | #nop 定义 MUDLIB,原则上,只有提供的游戏内容完全不一致才可以称为是不同的 MUDLIB。; 23 | #alias {mudlib.Define} { 24 | #local id {%1}; 25 | #local name {%2}; 26 | 27 | #var {gMUDLIB[$id]} {$name}; 28 | }; 29 | 30 | #nop 设置当前使用的 MUDLIB。这会导致后续加载的 MUD 相关的插件全部优先使用该 MUD 目录。; 31 | #alias {mudlib.Set} { 32 | #local id {%1}; 33 | 34 | #if { "$gMUDLIB[$id]" == "" } { 35 | errLog 未知的 MUDLIB $id,请先定义它。; 36 | #return; 37 | }; 38 | 39 | #var gCurrentMUDLIB {$id}; 40 | }; 41 | 42 | #class main close; 43 | -------------------------------------------------------------------------------- /framework/online.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:几个常用的别名 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #class main open; 14 | 15 | #alias {online.KeepAlive} {uptime}; 16 | 17 | #tick {online.KeepAlive} {online.KeepAlive} {60}; 18 | 19 | load-lib ga; 20 | load-module silent; 21 | load-module basic/char; 22 | load-module basic/map; 23 | load-module shortcut; 24 | 25 | tmux.SetTheme GAME; 26 | 27 | event.Emit user-online; 28 | 29 | #nop 提供给用户的自动执行函数; 30 | user-online; 31 | 32 | #event {RECEIVED OUTPUT} { 33 | #undelay online.WatchDog; 34 | #delay online.WatchDog {#zap} 180; 35 | }; 36 | 37 | #class main close; 38 | -------------------------------------------------------------------------------- /framework/settings.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:PaoTin++ 环境设置 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #nop 默认使用 GBK 编码,国内最常见的编码。支持通过触发动态切换编码。; 14 | #config {charset} {GBK1TOUTF8}; 15 | #nop 网络延迟较大的时候,设置较大的包延迟选项可以尽量避免命令输出接受不完整。; 16 | #config {packet patch} {0.8}; 17 | #nop 颜色代码的影响不会持续到下一行,防止未闭合的文字特效污染后续文本。; 18 | #config {color patch} {on}; 19 | #nop 按回车键重复输入之前的命令。; 20 | #config {repeat enter} {on}; 21 | 22 | #nop 输入的命令需要回显。; 23 | #config {command echo} {off}; 24 | #nop 定制一下输入命令的回显颜色,以示区分; 25 | #config {command color} {\e[4;38;5;178m}; 26 | 27 | #nop 关闭 MCCP 协议。; 28 | #config {mccp} {off}; 29 | 30 | #nop 开启 wordwrap 常常导致折行的文本显示为乱码,因此要关掉它; 31 | #config {wordwrap} {off}; 32 | 33 | #var TTYPE {TinTin++}; 34 | 35 | #nop 两处 oneshot 处理都是故意为之,目的是为了不影响 TinTin++ 内部状态。; 36 | #line oneshot #event {IAC SB TTYPE} { 37 | #send {\xFF\xFA\x18\x00\x50\x61\x6f\x54\x69\x6e\x2b\x2b/$TTYPE\xFF\xF0\}; 38 | #line oneshot #event {CATCH SEND OUTPUT} {#0}; 39 | }; 40 | 41 | #nop 切分出专门的文字输入区。; 42 | #split; 43 | -------------------------------------------------------------------------------- /framework/utils.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:框架依赖的工具函数 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #func {existsSession} { 14 | #local session {%1}; 15 | #if { !@existsFunction{$session} && {@{$session}{true}} == {true} } { 16 | #return 1; 17 | }; 18 | #else { 19 | #return 0; 20 | }; 21 | }; 22 | 23 | #func {existsVar} { 24 | #local var {%1}; 25 | #if { &{$var} > 0 } { 26 | #return 1; 27 | }; 28 | #else { 29 | #return 0; 30 | }; 31 | }; 32 | 33 | #func {existsAlias} { 34 | #local pName {%1}; 35 | #local pClass {%2}; 36 | 37 | #info alias save; 38 | 39 | #local idx {}; 40 | #foreach {*{info[ALIASES][]}} {idx} { 41 | #local name {$info[ALIASES][$idx][arg1]}; 42 | #local class {$info[ALIASES][$idx][class]}; 43 | 44 | #if { "$name" == "$pName" && ( "$pClass" == "" || "$class" == "$pClass" ) } { 45 | #return 1; 46 | }; 47 | }; 48 | 49 | #return 0; 50 | }; 51 | 52 | #func {existsFunction} { 53 | #local pName {%1}; 54 | #local pClass {%2}; 55 | 56 | #info function save; 57 | 58 | #local idx {}; 59 | #foreach {*{info[FUNCTIONS][]}} {idx} { 60 | #local name {$info[FUNCTIONS][$idx][arg1]}; 61 | #local class {$info[FUNCTIONS][$idx][class]}; 62 | 63 | #if { "$name" == "$pName" && ( "$pClass" == "" || "$class" == "$pClass" ) } { 64 | #return 1; 65 | }; 66 | }; 67 | 68 | #return 0; 69 | }; 70 | 71 | #func {existsJobPlugin} { 72 | #local job {%1}; 73 | #return {@existsPlugin{job/$job}}; 74 | }; 75 | 76 | #func {existsPlugin} { 77 | #local plugin {%1}; 78 | 79 | #if { @existsFile{plugins/${plugin}.tin} } { 80 | #return 1; 81 | }; 82 | 83 | #if { @existsFile{plugins/$plugin/__init__.tin} } { 84 | #return 1; 85 | }; 86 | 87 | #if { @existsFile{plugins/$plugin/__main__.tin} } { 88 | #return 1; 89 | }; 90 | 91 | #return 0; 92 | }; 93 | 94 | #func {mkdir} { 95 | #local dir {%1}; 96 | #line quiet #log make {$dir}; 97 | #return @existsDir{$dir}; 98 | }; 99 | 100 | #func {uuid} { 101 | #local now {}; 102 | #local random {}; 103 | 104 | #math random {1d1000}; 105 | #format random {%%03d} {$random}; 106 | #format now {%U}; 107 | 108 | #return {${now}.$random}; 109 | }; 110 | 111 | #alias {load-config} { 112 | #local config {%1}; 113 | 114 | #if { @isEmpty{$config} } { 115 | xtt.Usage load-config; 116 | #return; 117 | }; 118 | 119 | load-file etc/${config}.tin; 120 | }; 121 | 122 | #func {getenv} { 123 | #local name {%1}; 124 | 125 | #info environ save; 126 | #return {$info[ENVIRON][$name]}; 127 | }; 128 | 129 | #func {uname} { 130 | #info system save; 131 | #local uname {$info[SYSTEM][OS]}; 132 | #unvar info[SYSTEM]; 133 | 134 | #if { "$uname" == "UNKNOWN" } { 135 | #line quiet #script uname {uname -s}; 136 | #if { &uname[] == 1 } { 137 | #local uname {$uname[1]}; 138 | }; 139 | #else { 140 | #local uname {UNKNOWN}; 141 | }; 142 | }; 143 | 144 | #if { "$uname" == "Linux" } { 145 | #if { @existsFile{/.dockerenv} } { 146 | #local uname {Docker}; 147 | }; 148 | #elseif { @existsDir{/data/data/com.termux/files} } { 149 | #local uname {Android}; 150 | }; 151 | #elseif { @existsFile{/ish/version} } { 152 | #local uname {iOS}; 153 | }; 154 | #elseif { @existsFile{/proc/sys/fs/binfmt_misc/WSLInterop} } { 155 | #line quiet #script uname {uname -r}; 156 | #local uname {$uname[1]}; 157 | #if { "$uname" == "%*Microsoft%*" } { 158 | #local uname {WSL1}; 159 | }; 160 | #elseif { "$uname" == "%*microsoft%*" } { 161 | #local uname {WSL2}; 162 | }; 163 | #else { 164 | #local uname {WSL}; 165 | }; 166 | }; 167 | }; 168 | 169 | #return {$uname}; 170 | }; 171 | 172 | #func {osname} { 173 | #local uname {@uname{}}; 174 | #if { "$uname" == "Darwin" } { 175 | #return {macOS}; 176 | }; 177 | 178 | #if { "$uname" != "Linux" } { 179 | #return {$uname}; 180 | }; 181 | 182 | #local tmp {}; 183 | #line quiet #scan file {/etc/os-release} { 184 | #regex {&1} {^ID={[^\n]*}\n} {#var tmp {&&1}} 185 | }; 186 | #replace tmp {"} {}; 187 | #replace tmp {%*{-| }%*} {&1}; 188 | #switch {"$tmp"} { 189 | #case {""} {#0}; 190 | #case {"opensuse"} {#local uname {openSUSE}}; 191 | #case {"alpine"} {#local uname {@if{@existsFile{/.dockerenv};Docker;Alpine}}}; 192 | #default {#local uname {@str.Capital{$tmp}}}; 193 | }; 194 | #unvar tmp; 195 | 196 | #return {$uname}; 197 | }; 198 | 199 | #var TTYPE {@osname{}}; 200 | 201 | #func {uptime} { 202 | #info session save; 203 | 204 | #local now {0}; 205 | #format now {%T}; 206 | 207 | #local uptime {0}; 208 | #math uptime {$now - $info[SESSION][CREATED]}; 209 | #return {$uptime}; 210 | }; 211 | -------------------------------------------------------------------------------- /ids/DEFAULT: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:默认环境信息 5 | 模块说明:本文件属于框架代码的一部分,一般情况不建议修改。如果需要定制,请在用户配置里进行。 6 | 用户配置文件的模版请参见 ids/EXAMPLE 7 | 版权声明:本文件属于 PaoTin++ 的一部分 8 | =========== 9 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 10 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 11 | =========== 12 | */ 13 | 14 | mudlib.Set {pkuxkx}; 15 | 16 | #nop session 的名字,没特殊需求不建议修改; 17 | #var session[name] pkuxkx; 18 | 19 | #nop session 的主机名,海外用户需要用 pkuxkx.com 或 hk.pkuxkx.com; 20 | #var session[host] hk.pkuxkx.com; 21 | #var session[host] pkuxkx.com; 22 | #var session[host] mud.pkuxkx.net; 23 | 24 | #nop session 的端口,按照论坛上面的地址修改; 25 | #var session[port] 8081; 26 | #var session[UTF8] true; 27 | 28 | #nop 连接断开时,间隔多少秒自动重连; 29 | #var session[reconnect] 12; #nop 正常间隔; 30 | #var session[reconnect_slow] 600; #nop 维护模式下的间隔; 31 | #var session[remote_maint] false; #nop 维护模式开关; 32 | 33 | #nop 日志相对路径,留空则使用 user[id] 的值; 34 | #nop 如果有同一个 ID 连接不同站点的需求,就应当在 ID 配置文件里定制它; 35 | #var session[log_path] {}; 36 | 37 | #nop 可以通过触发来开启维护模式; 38 | #action {^%*(%*)告诉你:开启远程维护$} {#var session[remote_maint] {true}}; 39 | 40 | #nop 你的账号英文 ID,请在单独的文件里设置,这里只是占个位置; 41 | #var user[id] bot; 42 | 43 | #nop 你的账号密码,请在单独的文件里设置,这里只是占个位置; 44 | #var user[passwd] 123456; 45 | 46 | #nop 是否手动登录,默认为否,即自动登录; 47 | #var user[manual] {false}; 48 | 49 | #nop 是否开启 GMCP,默认开启; 50 | #var user[GMCP] true; 51 | 52 | #nop 是否开启鼠标支持,默认开启; 53 | #var user[MOUSE] true; 54 | 55 | #nop 默认的食物和饮料; 56 | #var char[favorite][food] {gan liang}; 57 | #var char[favorite][water] {niurou tang}; 58 | 59 | #nop 用户上线之后想要自动执行的代码写这里; 60 | #alias {user-online} { 61 | back; 62 | }; 63 | -------------------------------------------------------------------------------- /ids/EXAMPLE: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tt++ 2 | #nop vim: set filetype=tt:; 3 | 4 | /* 5 | 模块名称:用户配置文件模版 6 | 模块说明:本文件是 PaoTin++ 的命令行入口程序 7 | 版权声明:本文件属于 PaoTin++ 的一部分 8 | =========== 9 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 10 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 11 | =========== 12 | */ 13 | 14 | #nop 载入 PaoTin++ 环境; 15 | #read {framework/main.tin}; 16 | 17 | #nop 游戏入口,服务器和端口需要分别设置。; 18 | #var session[host] mud.pkuxkx.net; 19 | #var session[port] 8081; 20 | #nop 你的账号英文 ID; 21 | #var user[id] foo; 22 | #nop 你的大名; 23 | #var user[name] 小虾米; 24 | #nop 你的账号密码; 25 | #var user[passwd] bar; 26 | 27 | #nop XXX: 所有 ids/DEFAULT 里的内容都可以在这里定制; 28 | #nop 这里为了简单起见没有全部列出,如有更多需求请参考 ids/DEFAULT 定制; 29 | 30 | #nop 用户上线之后想要自动执行的代码写这里; 31 | #alias {user-online} { 32 | chat* back; 33 | }; 34 | 35 | #nop 也可以在下面继续定义自己的别名和触发,均在 #gts 生效(会继承到所有 session); 36 | 37 | #alias {hello} {hi}; 38 | -------------------------------------------------------------------------------- /ids/paotin: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tt++ 2 | #nop vim: filetype=tt 3 | 4 | /* 5 | 本文件属于 PaoTin++ 的一部分。 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | */ 9 | 10 | #read framework/main.tin; 11 | 12 | #var user[id] LOCAL; 13 | #var user[name] 本地测试; 14 | 15 | #alias {auto-login} { 16 | setup; 17 | greeting; 18 | }; 19 | 20 | #alias {setup} { 21 | #line quiet #port init null 0; 22 | #untick {KeepAlive}; 23 | load-lib event; 24 | load-lib ui; 25 | }; 26 | 27 | #alias {greeting} { 28 | okLog 欢迎使用 PaoTin++!; 29 | okLog 输入 <134>HELP paotin<129> 和 <134>HELP xtintin<129> 可以开始了解 PaoTin++。; 30 | okLog 更多学习资料和社区资源可以查看 <134>HELP NEWBIE<129>。<131>所有命令注意区分大小写<129>。; 31 | }; 32 | 33 | #alias {test} { 34 | load-module foo; 35 | }; 36 | 37 | #alias {%*} {#showme 你奋笔疾书,挥毫落纸:「<060>%0<070>」} {9.999}; 38 | -------------------------------------------------------------------------------- /ids/pkuxkx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tt++ 2 | #nop vim: set filetype=tt:; 3 | 4 | /* 5 | 模块名称:用户配置文件模版 6 | 模块说明:本文件是 PaoTin++ 的命令行入口程序 7 | 版权声明:本文件属于 PaoTin++ 的一部分 8 | =========== 9 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 10 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 11 | =========== 12 | */ 13 | 14 | #nop 载入 PaoTin++ 环境; 15 | #read {framework/main.tin}; 16 | 17 | #nop 你要连哪个接入,海外用户需要用 pkuxkx.com 或 hk.pkuxkx.com; 18 | #var session[host] mud.pkuxkx.net; 19 | #nop 你的账号英文 ID; 20 | #var user[id] {这里写你的游戏账号,例如 pkuxkx}; 21 | #nop 你的大名; 22 | #var user[name] {这里写你的游戏中文名,例如 北大侠客行}; 23 | #nop 你的账号密码; 24 | #var user[passwd] {这里写你的游戏密码,例如 123456}; 25 | #nop 上面三处修改完毕之后,请删除下面这行文字:; 26 | #var user[manual] {true}; 27 | 28 | #nop XXX: 所有 ids/DEFAULT 里的内容都可以在这里定制; 29 | #nop 这里为了简单起见没有全部列出,如有更多需求请参考 ids/DEFAULT 定制; 30 | 31 | #nop 用户上线之后想要自动执行的代码写这里; 32 | #alias {user-online} { 33 | chat* back; 34 | }; 35 | 36 | #nop 也可以在下面继续定义自己的别名和触发,均在 #gts 生效(会继承到所有 session); 37 | 38 | #alias {hello} {hi}; 39 | -------------------------------------------------------------------------------- /ids/thuxyj: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tt++ 2 | #nop vim: set filetype=tt:; 3 | 4 | /* 5 | 模块名称:用户配置文件模版 6 | 模块说明:本文件是 PaoTin++ 的命令行入口程序 7 | 版权声明:本文件属于 PaoTin++ 的一部分 8 | =========== 9 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 10 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 11 | =========== 12 | */ 13 | 14 | #nop 载入担子炮的 TinTin 脚本框架; 15 | #read {framework/main.tin}; 16 | 17 | mudlib.Set thuxyj; 18 | 19 | #nop 你要连哪个接入; 20 | #var session[name] thuxyj; 21 | #var session[host] xyj.thu.cn; 22 | #var session[port] 6666; 23 | #var session[UTF8] false; 24 | #nop 你的账号英文 ID; 25 | #var user[id] {花括号里写你的游戏账号,例如 thuxyj}; 26 | #nop 你的大名; 27 | #var user[name] {花括号里写你的游戏中文名,例如 清华西游记}; 28 | #nop 你的账号密码; 29 | #var user[passwd] {花括号里写你的游戏密码,例如 123456}; 30 | #nop 上面三处修改完毕之后,请删除下面这行文字:; 31 | #var user[manual] {true}; 32 | 33 | #nop XXX: 所有 ids/DEFAULT 里的内容都可以在这里定制; 34 | #nop 这里为了简单起见没有全部列出,如有更多需求请参考 ids/DEFAULT 定制; 35 | 36 | #nop 用户上线之后想要自动执行的代码写这里; 37 | #alias {user-online} { 38 | chat* back; 39 | }; 40 | 41 | #nop 也可以在下面继续定义自己的别名和触发,均在 #gts 生效(会继承到所有 session); 42 | 43 | #alias {hello} {hi}; 44 | -------------------------------------------------------------------------------- /init.tin: -------------------------------------------------------------------------------- 1 | #read framework/settings.tin; 2 | #config {command echo} {on}; 3 | #echo {<131>请注意:这不是 PaoTin++!这只是担子炮魔改版的 TinTin++。<099>}; 4 | #echo {<129>退出请输入 <134>#end<129> 帮助请输入 <134>#help<129> 小乖乖中文手册最新版: <099>https://github.com/zixijian/tt/blob/master/Wiki.md}; 5 | #echo {<129>连接北大侠客行请输入 <134>#session mud mud.pkuxkx.net 8080<099>}; 6 | #echo {<129>连接清华西游记请输入 <134>#session mud xyj.thu.cn 8666<099>}; 7 | -------------------------------------------------------------------------------- /init.vim: -------------------------------------------------------------------------------- 1 | silent! call plug#begin() 2 | Plug 'dzpao/vim-mbs' 3 | Plug 'morhetz/gruvbox' 4 | Plug 'yegappan/mru' 5 | Plug 'jlanzarotta/BufExplorer' 6 | call plug#end() 7 | 8 | " 不兼容 vi 9 | set nocompatible 10 | " 用空格代替 TAB 11 | set expandtab 12 | " 整体左移或者右移时,每次 4 个空格 13 | set shiftwidth=4 14 | " TAB 键按 4 空格对齐 15 | set tabstop=4 16 | set softtabstop=4 17 | set smarttab 18 | 19 | " 开启智能缩进 20 | set smartindent 21 | 22 | " 智能缩进开启时,不让 # 删除自动缩进。 23 | inoremap # X# 24 | 25 | " 开启真彩色 26 | set termguicolors 27 | " 开启真彩色配色方案 28 | colorscheme gruvbox 29 | highlight Normal guibg=#1d2021 30 | " 显示行号 31 | set number 32 | " 显示相对行号 33 | set relativenumber 34 | " 显示光棒 35 | set cursorline 36 | " 80 列高亮,提醒边界线 37 | set colorcolumn=80 38 | 39 | " 文件写入成功后,不保留备份文件 40 | set nobackup 41 | " 直接写原文件,不先建立备份 42 | set nowritebackup 43 | " 不要交换文件 44 | set noswapfile 45 | 46 | " 开启鼠标支持 47 | set mouse=nvi 48 | 49 | " 开启自动缩进 50 | set cindent 51 | 52 | " 自动识别文件编码 53 | set fileencodings=utf8,gbk 54 | 55 | " 始终用 utf8 显示 56 | set encoding=utf8 57 | 58 | " 开启 modeline 支持 59 | set modeline 60 | 61 | " F 开启 Buffer Explorer 62 | nmap F :BufExplorer 63 | nnoremap ,F F 64 | nnoremap ,f F 65 | autocmd BufEnter \[BufExplorer\] nmap F q 66 | 67 | " M 开启/关闭 MRU 68 | nmap M :MRU 69 | nnoremap ,M M 70 | autocmd BufEnter __MRU_Files__ nnoremap M :q 71 | autocmd BufEnter -RecentFiles- nnoremap M :q 72 | 73 | nmap gt 74 | nmap gT 75 | -------------------------------------------------------------------------------- /install.nsi: -------------------------------------------------------------------------------- 1 | Name "PaoTin++ for Windows" 2 | OutFile setup.exe 3 | ShowInstDetails show 4 | AllowRootDirInstall true 5 | 6 | !include "FileFunc.nsh" 7 | !include "nsDialogs.nsh" 8 | !include "winmessages.nsh" 9 | 10 | Page custom SelectDriver 11 | Page instfiles 12 | 13 | var dialog 14 | var hctrl 15 | var drive 16 | 17 | Function getDrivesCallback 18 | ${NSD_CB_AddString} $hctrl "$9" 19 | Push $0 20 | FunctionEnd 21 | 22 | Function changeSelect 23 | ${NSD_GetText} $hctrl $0 24 | StrCpy $drive $0 25 | FunctionEnd 26 | 27 | Function SelectDriver 28 | nsDialogs::Create 1018 29 | Pop $dialog 30 | 31 | ${NSD_CreateLabel} 0 10u 100% 20u "Select which disk you want to install to:" 32 | Pop $hctrl 33 | ${NSD_CreateDropList} 0 30u 100% 80u "Select" 34 | Pop $hctrl 35 | 36 | ${GetDrives} "HDD" "getDrivesCallback" 37 | StrCpy $drive "C:\" 38 | ${NSD_CB_SelectString} $hctrl "$drive" 39 | 40 | ${NSD_OnChange} $hctrl "changeSelect" 41 | 42 | nsDialogs::Show 43 | FunctionEnd 44 | 45 | var file 46 | var dir 47 | 48 | Section 49 | StrCpy $drive $drive 2 50 | StrCpy $file "$TEMP\settings.json" 51 | FileOpen $R3 $file w 52 | FileWrite $R3 "{$\r$\n" 53 | FileWrite $R3 " $\"profiles$\": $\r$\n" 54 | FileWrite $R3 " {$\r$\n" 55 | FileWrite $R3 " $\"defaults$\": {},$\r$\n" 56 | FileWrite $R3 " $\"list$\": $\r$\n" 57 | FileWrite $R3 " [$\r$\n" 58 | FileWrite $R3 " {$\r$\n" 59 | FileWrite $R3 " $\"commandline$\": $\"$drive\\paotin\\bin\\tt++.exe ids/tintin$\",$\r$\n" 60 | FileWrite $R3 " $\"guid$\": $\"{0b778637-b3ef-410d-b678-3f825e2cdef1}$\",$\r$\n" 61 | FileWrite $R3 " $\"hidden$\": true,$\r$\n" 62 | FileWrite $R3 " $\"name$\": $\"WinTin++$\",$\r$\n" 63 | FileWrite $R3 " $\"startingDirectory$\": $\"$drive\\paotin$\"$\r$\n" 64 | FileWrite $R3 " },$\r$\n" 65 | FileWrite $R3 " {$\r$\n" 66 | FileWrite $R3 " $\"commandline$\": $\"$drive\\paotin\\bin\\tt++.exe ids/paotin$\",$\r$\n" 67 | FileWrite $R3 " $\"guid$\": $\"{0b778637-b3ef-410d-b678-3f825e2cdef2}$\",$\r$\n" 68 | FileWrite $R3 " $\"hidden$\": true,$\r$\n" 69 | FileWrite $R3 " $\"name$\": $\"PaoTin++ for Windows$\",$\r$\n" 70 | FileWrite $R3 " $\"startingDirectory$\": $\"$drive\\paotin$\"$\r$\n" 71 | FileWrite $R3 " },$\r$\n" 72 | FileWrite $R3 " {$\r$\n" 73 | FileWrite $R3 " $\"commandline$\": $\"$drive\\paotin\\bin\\tt++.exe ids/pkuxkx$\",$\r$\n" 74 | FileWrite $R3 " $\"guid$\": $\"{0b778637-b3ef-410d-b678-3f825e2cdef3}$\",$\r$\n" 75 | FileWrite $R3 " $\"hidden$\": false,$\r$\n" 76 | FileWrite $R3 " $\"name$\": $\"\u5317\u5927\u4fa0\u5ba2\u884c$\",$\r$\n" 77 | FileWrite $R3 " $\"startingDirectory$\": $\"$drive\\paotin$\"$\r$\n" 78 | FileWrite $R3 " },$\r$\n" 79 | FileWrite $R3 " {$\r$\n" 80 | FileWrite $R3 " $\"commandline$\": $\"$drive\\paotin\\bin\\tt++.exe ids/thuxyj$\",$\r$\n" 81 | FileWrite $R3 " $\"guid$\": $\"{0b778637-b3ef-410d-b678-3f825e2cdef4}$\",$\r$\n" 82 | FileWrite $R3 " $\"hidden$\": false,$\r$\n" 83 | FileWrite $R3 " $\"name$\": $\"\u6e05\u534e\u897f\u6e38\u8bb0$\",$\r$\n" 84 | FileWrite $R3 " $\"startingDirectory$\": $\"$drive\\paotin$\"$\r$\n" 85 | FileWrite $R3 " }$\r$\n" 86 | FileWrite $R3 " ]$\r$\n" 87 | FileWrite $R3 " }$\r$\n" 88 | FileWrite $R3 "}$\r$\n" 89 | FileClose $R3 90 | 91 | StrCpy $dir "$LocalAppData\Microsoft\Windows Terminal\Fragments\PaoTin++" 92 | CreateDirectory "$dir" 93 | CopyFiles /SILENT "$file" "$dir" 94 | RMDir "$LocalAppData\Microsoft\Windows Terminal Preview\Fragments\PaoTin++" 95 | SectionEnd 96 | 97 | Section 98 | StrCpy $INSTDIR "$drive\paotin" 99 | StrCpy $OUTDIR "$drive\paotin" 100 | SetOutPath "$INSTDIR" 101 | RMDir "$INSTDIR" 102 | File /r * 103 | 104 | StrCpy $file "$drive\paotin\etc\windows.tin" 105 | FileOpen $R3 $file w 106 | FileWrite $R3 "#var gPaoTinPath {$drive/paotin};$\r$\n" 107 | FileWrite $R3 "#var gPaoTinVarPath {$drive/my-paotin};$\r$\n" 108 | FileClose $R3 109 | SectionEnd 110 | 111 | !define CreateJunction "!insertmacro CreateJunction" 112 | 113 | Function CreateJunction 114 | Exch $4 115 | Exch 116 | Exch $5 117 | Push $1 118 | Push $2 119 | Push $3 120 | Push $6 121 | CreateDirectory "$5" 122 | System::Call "kernel32::CreateFileW(w `$5`, i 0x40000000, i 0, i 0, i 3, i 0x02200000, i 0) i .r6" 123 | 124 | ${If} $0 = "-1" 125 | StrCpy $0 "0" 126 | RMDir "$5" 127 | goto create_junction_end 128 | ${EndIf} 129 | 130 | CreateDirectory "$4" ; Windows XP requires that the destination exists 131 | StrCpy $4 "\??\$4" 132 | StrLen $0 $4 133 | IntOp $0 $0 * 2 134 | IntOp $1 $0 + 2 135 | IntOp $2 $1 + 10 136 | IntOp $3 $1 + 18 137 | System::Call "*(i 0xA0000003, &i4 $2, &i2 0, &i2 $0, &i2 $1, &i2 0, &w$1 `$4`, &i2 0)i.r2" 138 | System::Call "kernel32::DeviceIoControl(i r6, i 0x900A4, i r2, i r3, i 0, i 0, *i r4r4, i 0) i.r0" 139 | System::Call "kernel32::CloseHandle(i r6) i.r1" 140 | 141 | ${If} $0 == "0" 142 | RMDir "$5" 143 | ${EndIf} 144 | 145 | create_junction_end: 146 | Pop $6 147 | Pop $3 148 | Pop $2 149 | Pop $1 150 | Pop $5 151 | Pop $4 152 | FunctionEnd 153 | 154 | !macro CreateJunction Junction Target outVar 155 | Push $0 156 | Push "${Junction}" 157 | Push "${Target}" 158 | Call CreateJunction 159 | StrCpy ${outVar} $0 160 | Pop $0 161 | !macroend 162 | 163 | Section 164 | CreateDirectory "$drive\my-paotin" 165 | CreateDirectory "$drive\my-paotin\ids" 166 | CreateDirectory "$drive\my-paotin\etc" 167 | CreateDirectory "$drive\my-paotin\data" 168 | CreateDirectory "$drive\my-paotin\log" 169 | CreateDirectory "$drive\my-paotin\plugins" 170 | 171 | ${CreateJunction} "$drive\paotin\var" "$drive\my-paotin" $0 172 | SectionEnd 173 | -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mudclient/paotin/12711b19c3ac182a44a0fd88dfe95d6b7fe33ca3/log/.gitkeep -------------------------------------------------------------------------------- /mud/pkuxkx/etc/ui-chat.tin: -------------------------------------------------------------------------------- 1 | #list chat-channel create { 2 | {{pattern}{求助} {action}{helpmeLog} {gag}{true}} 3 | {{pattern}{北侠QQ群} {action}{qqLog} {gag}{true}} 4 | {{pattern}{{闲聊|副本|谣言}} {action}{chatLog} {gag}{true}} 5 | {{pattern}{{门派|帮派|队伍}} {action}{chatLog} {gag}{true}} 6 | {{pattern}{{江湖|任务|交易}} {action}{jhLog} {gag}{true}} 7 | {{pattern}{{本地|区域|亡灵}} {action}{bdLog} {gag}{true}} 8 | {{pattern}{{表决|醒目}} {action}{chatLog} {gag}{false}} 9 | {{pattern}{{答问如流|备选答案}} {action}{answerLog} {gag}{false}} 10 | {{pattern}{私聊} {action}{tellLog} {gag}{false}} 11 | }; 12 | 13 | #nop 在这里设置江湖帮派名称列表; 14 | #var chat-group-name {斧头帮;百花谷;牛肉面馆;龍野客栈ゞ;万梅山庄;太白楼;广寒宫;魂殿;桃林书院;黑手党;重生}; 15 | -------------------------------------------------------------------------------- /mud/pkuxkx/etc/ui-settings.extra.tin: -------------------------------------------------------------------------------- 1 | #var tmux-update-events[DEFAULT] {char/score}; 2 | #var tmux-pane-border-format[DEFAULT] { 3 | {left} {ID: [<188>\$char[档案][账号]<099>]} 4 | {centre} {大名: [\$char[档案][彩色大名]<099>]} 5 | {right} {头衔: [\$char[档案][彩色头衔]<099>]} 6 | }; 7 | 8 | #nop {Top} {TopSepBar} {MidSepBar} {Bot} {BotSepBar}; 9 | #list prompt-fields create { 10 | { {place}{MidSepBar} {label}{重启} {name}{reboot} {countdown}{Auto} } 11 | { {place}{MidSepBar} {label}{经验转化率} {name}{pot/exp} } 12 | { {place}{MidSepBar} {label}{签到} {name}{sign} } 13 | { {place}{MidSepBar} {label}{铜雀台} {name}{tqt} } 14 | 15 | { {place}{MidSepBar} {label}{衣钵传承} {name}{yibo} } 16 | { {place}{MidSepBar} {label}{药炉} {name}{stove} } 17 | 18 | { {place}{MidSepBar} {label}{亲戚} {name}{renqin} } 19 | { {place}{MidSepBar} {label}{燕青拳} {name}{yanqing} } 20 | 21 | { {place}{MidSepBar} {label}{保卫} {name}{baowei} } 22 | { {place}{MidSepBar} {label}{答题} {name}{dati} } 23 | { {place}{MidSepBar} {label}{比武} {name}{biwu} {countdown}{Auto} } 24 | { {place}{MidSepBar} {label}{福米} {name}{fullme} {cooldown}{600} } 25 | { {place}{MidSepBar} {label}{URL} {name}{URL} {cooldown}{180} } 26 | 27 | { {place}{Bot} {line}{1} {label}{经验} {name}{exp} {width}{8} {visibility}{Always} } 28 | { {place}{Bot} {line}{1} {label}{本次新增} {name}{expDelta} {width}{6} {visibility}{HideLabel} {cooldown}{600} } 29 | { {place}{Bot} {line}{1} {label}{增速} {name}{expSpd} {width}{8} {cooldown}{600} } 30 | { {place}{Bot} {line}{1} {label}{潜能} {name}{pot} {width}{8} {visibility}{Always} } 31 | { {place}{Bot} {line}{1} {label}{本次新增} {name}{potDelta} {width}{6} {visibility}{HideLabel} {cooldown}{600} } 32 | { {place}{Bot} {line}{1} {label}{气血恢复} {name}{yunqi} {width}{5} {cooldown}{60} } 33 | { {place}{Bot} {line}{1} {label}{存款} {name}{saving} {width}{10} {cooldown}{1800} } 34 | { {place}{Bot} {line}{1} {label}{现金} {name}{cash} {width}{10} {cooldown}{600} } 35 | { {place}{Bot} {line}{1} {label}{收益速度} {name}{profit} {width}{10} {cooldown}{600} } 36 | 37 | { {place}{Bot} {line}{2} {label}{不利战况} {name}{chousui} {visibility}{Always} {cooldown}{60} } 38 | { {place}{Bot} {line}{2} {label}{透骨钉} {name}{tougu} {visibility}{HideLabel} {cooldown}{60} } 39 | { {place}{Bot} {line}{2} {label}{刺穴} {name}{cixue} {visibility}{HideLabel} {cooldown}{60} } 40 | { {place}{Bot} {line}{2} {label}{刺目} {name}{cimu} {visibility}{HideLabel} {cooldown}{10} {color}{<179><579>} } 41 | { {place}{Bot} {line}{2} {label}{刺腕} {name}{ciwan} {visibility}{HideLabel} {cooldown}{10} {color}{<519>} } 42 | { {place}{Bot} {line}{2} {label}{内力不济} {name}{neili} {visibility}{HideLabel} {cooldown}{10} {color}{<119><519>} } 43 | 44 | { {place}{Bot} {line}{2} {label}{中毒情况} {name}{bingpo1} {visibility}{Always} {cooldown}{60} } 45 | { {place}{Bot} {line}{2} {label}{星宿毒掌} {name}{duzhang1} {visibility}{HideLabel} {cooldown}{60} } 46 | { {place}{Bot} {line}{2} {label}{情毒} {name}{qingdu1} {visibility}{HideLabel} {cooldown}{60} } 47 | { {place}{Bot} {line}{2} {label}{凝血神爪毒} {name}{zhuadu1} {visibility}{HideLabel} {cooldown}{60} } 48 | { {place}{Bot} {line}{2} {label}{生死符} {name}{ssfu1} {visibility}{HideLabel} {cooldown}{60} } 49 | 50 | { {place}{Bot} {line}{2} {label}{有利战况} {name}{ssfu2} {visibility}{Always} {cooldown}{60} } 51 | { {place}{Bot} {line}{2} {label}{日魂激发} {name}{rihun} {visibility}{HideLabel} {cooldown}{60} } 52 | { {place}{Bot} {line}{2} {label}{毒免} {name}{dumian} {visibility}{HideLabel} {cooldown}{60} } 53 | 54 | { {place}{Bot} {line}{3} {label}{状态} {name}{status} {cooldown}{600} } 55 | { {place}{Bot} {line}{3} {label}{持续效果} {name}{persist} {countdown}{Seconds} } 56 | 57 | { {place}{Bot} {line}{99} {label}{<119>警报} {name}{alert} } 58 | { {place}{Bot} {line}{99} {label}{随手记} {name}{note} {color}{<134>}} 59 | 60 | { {place}{BotSepBar} {label}{搜索条件} {name}{search} } 61 | { {place}{BotSepBar} {label}{屏幕美化} {name}{beautify} {cooldown}{600} {visibility}{HideCool} } 62 | { {place}{BotSepBar} {label}{状态栏更新} {name}{disable} {visibility}{HideLabel} } 63 | { {place}{BotSepBar} {label}{任务} {name}{job} } 64 | { {place}{BotSepBar} {label}{阶段} {name}{stage} {cooldown}{1200} {countdown}{Auto} } 65 | { {place}{BotSepBar} {label}{区域} {name}{area} {cooldown}{1200} } 66 | { {place}{BotSepBar} {label}{地点} {name}{room} {cooldown}{1200} } 67 | { {place}{BotSepBar} {label}{目标} {name}{target} } 68 | { {place}{BotSepBar} {label}{类型} {name}{type} } 69 | }; 70 | 71 | #nop 热键绑定; 72 | #list global-key-bindings create { 73 | {{key}{\cd} {meaning}{命令行智能切换} {action}{cli.SmartToggle}} 74 | {{key}{\co\cd} {meaning}{禁止/允许命令和触发} {action}{xtt.ToggleDisableCommands}} 75 | {{key}{\cos} {meaning}{禁止/允许状态栏更新} {action}{prompt.ToggleSwitch}} 76 | {{key}{\cob} {meaning}{禁止/允许界面宽字符美化} {action}{beautify.ToggleSwitch}} 77 | {{key}{\col} {meaning}{查看选项列表} {action}{option.List}} 78 | {{key}{\cod} {meaning}{开启/关闭调试模式} {action}{option.Toggle DebugLog}} 79 | {{key}{\coD} {meaning}{开启/关闭调试日志} {action}{option.Toggle DebugLogEcho}} 80 | {{key}{\coe} {meaning}{开启/关闭命令发送回显} {action}{option.Toggle EchoCommand}} 81 | {{key}{\com} {meaning}{禁止/允许屏蔽他人移动信息} {action}{option.Toggle GagMove}} 82 | {{key}{\cog} {meaning}{开启/关闭 GMCP 调试信息} {action}{option.Toggle GMCPDebug}} 83 | {{key}{\coM} {meaning}{开启/关闭地图调试模式} {action}{option.Toggle MapDebug; look}} 84 | {{key}{\coV} {meaning}{开启/禁止显示房间风景图} {action}{option.Toggle ShowRoomView; look}} 85 | }; 86 | -------------------------------------------------------------------------------- /mud/pkuxkx/framework/online.extra.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:几个常用的别名 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #nop 武当、少林等门派的诵经文本会干扰机器运行,使用全局替换从一开始就屏蔽掉。; 14 | #substitute {~\e[1;36m%+1u..\e[2;37;0m} {}; 15 | 16 | #alias {online.KeepAlive} { 17 | #local blankly {true}; 18 | #if { @char.IsBusy{} || @char.InCombat{} && $char[HP][食物] > 0 && $char[HP][饮水] > 0 } { 19 | #nop Busy 中无法吃喝,或者战斗中除非快要饿死了否则也不吃喝。; 20 | }; 21 | #else { 22 | #if { $char[HP][食物] < $char[HP][最大食物] / 3 } { 23 | #if { "$char[favorite][food]" != "{NOTHING|}" } { 24 | xtt.Send {eat $char[favorite][food]}; 25 | #local blankly {false}; 26 | }; 27 | }; 28 | #if { $char[HP][饮水] < $char[HP][最大饮水] / 3 } { 29 | #if { "$char[favorite][water]" != "NOTHING" } { 30 | xtt.Send {drink $char[favorite][water]}; 31 | #local blankly {false}; 32 | }; 33 | }; 34 | }; 35 | #if { @isTrue{$blankly} } { 36 | ga.Sync; 37 | }; 38 | }; 39 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/busy.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | ///=== { 10 | ///// busy 模块向其它模块提供 busy 检查机制。 11 | ///// }; 12 | 13 | #var basic_busy[META] { 14 | {NAME} {busy检查} 15 | {DESC} {检查角色是否处于 busy 状态,并在 busy 结束时回调代码} 16 | {AUTHOR} {担子炮} 17 | }; 18 | 19 | #func {basic_busy.Init} { 20 | event.Handle {char/nobusy} {busy.on-idle} {basic/busy} {busy.on-idle}; 21 | #return {true}; 22 | }; 23 | 24 | VAR {正在等待的回调列表} busy.wait-list {}; 25 | 26 | ///=== { 27 | // ## busy.Wait <回调代码> 28 | // 检查角色是否处于 busy 状态,如果是,则等待 busy 状态结束后执行回调代码。 29 | // 否则立即执行回调代码。 30 | // }; 31 | #alias {busy.Wait} { 32 | #local code {%1}; 33 | 34 | #local len {&busy.wait-list[]}; 35 | #list busy.wait-list add {{$code}}; 36 | 37 | #if { $len == 0 } { 38 | #if { @isTrue{$char[HP][忙]} } { 39 | #delay busy.checkbusy {checkbusy} 10; 40 | }; 41 | #else { 42 | #delay busy.checkbusy {checkbusy} 0; 43 | }; 44 | }; 45 | }; 46 | 47 | ///=== { 48 | // ## busy.Halt <回调代码> 49 | // 检查角色是否处于 busy 状态,如果是,则用 halt 解除 busy,解除成功后,执行回调代码。 50 | // 否则立即执行回调代码。 51 | // }; 52 | #alias {busy.Halt} { 53 | #local code {%1}; 54 | 55 | #if { "$code" == "" } { 56 | #local code {#untick busy.Halt}; 57 | }; 58 | #else { 59 | #local code { 60 | #untick busy.Halt; 61 | $code; 62 | }; 63 | }; 64 | 65 | busy.Wait {$code}; 66 | 67 | #if { @isTrue{$char[HP][忙]} } { 68 | halt; 69 | }; 70 | 71 | #tick busy.Halt {halt; checkbusy} 1; 72 | }; 73 | 74 | #alias {busy.on-idle} { 75 | #undelay busy.checkbusy; 76 | #undelay busy.on-idle; 77 | #untick busy.Halt; 78 | 79 | #local todo {$busy.wait-list}; 80 | #var busy.wait-list {}; 81 | 82 | #while { &todo[] > 0 } { 83 | #local code {$todo[1]}; 84 | #list todo delete 1; 85 | $code 86 | }; 87 | }; 88 | 89 | #action {^你正忙{|ID=basic/busy}$} { 90 | #undelay busy.on-idle; 91 | 92 | char.MarkBusy; 93 | 94 | #if { &busy.wait-list[] == 0 } { 95 | #return; 96 | }; 97 | 98 | #if { @isTrue{$gGMCP[Channels][Status]} } { 99 | #delay busy.checkbusy {checkbusy} 10; 100 | }; 101 | #else { 102 | #delay busy.checkbusy {checkbusy} 1; 103 | }; 104 | 105 | #line gag; 106 | }; 107 | 108 | #action {^你不忙{|ID=basic/busy}$} { 109 | #undelay busy.checkbusy; 110 | char.UnmarkBusy; 111 | #delay {busy.on-idle} {busy.on-idle} 0; 112 | #line gag; 113 | }; 114 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/char/__init__.tin: -------------------------------------------------------------------------------- 1 | #nop 不要删除本行,TinTin 语法要求文件必须以井号开始; 2 | 3 | load-module {basic/char/hp}; 4 | load-module {basic/char/skills}; 5 | load-module {basic/char/status}; 6 | load-module {basic/char/i2}; 7 | load-module {basic/char/sachet}; 8 | load-module {basic/char/score}; 9 | load-module {basic/char/gmcp}; 10 | load-module {basic/char/jifa}; 11 | load-module {basic/char/special}; 12 | 13 | event.HandleOnce {user-online} {char/init} {basic/char} { 14 | set table_pattern 1; 15 | }; 16 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/char/i2.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分 5 | =========== 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | =========== 9 | */ 10 | 11 | #var basic_char_i2[META] { 12 | {NAME} {背包信息} 13 | {DESC} {解析背包内容,识别随身物品、穿戴、装备信息,并储存到变量} 14 | {AUTHOR} {担子炮} 15 | {NOTE} {本文件属于 PaoTin++ 的一部分} 16 | }; 17 | 18 | /* 19 | i2 格式: 20 | 你身上带著下列这些东西(负重 27%): 21 | 九十一两白银(Silver) 22 | 一粒牛黃血竭丹(Xuejie dan) 23 | 二个竹壶(Zhuhu) 24 | 三块干粮(Gan liang) 25 | 大砍刀(Dakan dao) 26 | 青锋剑(Qingfeng sword) 27 | 北侠战袍(Pkuxkx zhanpao) 28 | 北侠战甲(Pkuxkx zhanjia) 29 | 北侠战衣(Pkuxkx zhanyi) 30 | 北侠战盔(Pkuxkx zhankui) 31 | 北侠战靴(Pkuxkx zhanxue) 32 | 锦囊(Jin nang) 33 | 34 | ------------------------------------------- 35 | 你左手拿着:大砍刀(Dakan dao) 36 | 你右手拿着:青锋剑(Qingfeng sword) 37 | 38 | ------------------------------------------- 39 | 你身上穿着: 40 | 北侠战袍(Pkuxkx zhanpao) 41 | 北侠战甲(Pkuxkx zhanjia) 42 | 北侠战衣(Pkuxkx zhanyi) 43 | 北侠战盔(Pkuxkx zhankui) 44 | 北侠战靴(Pkuxkx zhanxue) 45 | 锦囊(Jin nang) 46 | */ 47 | 48 | #func {basic_char_i2.Init} { 49 | char.reset.i2; 50 | char.reset.bag; 51 | char.reset.sachet; 52 | #return true; 53 | }; 54 | 55 | #alias {char.reset.i2} { 56 | #class data/basic/char open; 57 | #nop 随身物品; 58 | #var char-Item {}; 59 | #nop 穿戴; 60 | #var char-Wear {}; 61 | #nop 武器; 62 | #var char-Wield {}; 63 | #nop 装备; 64 | #var char-Equip {}; 65 | #class data/basic/char close; 66 | }; 67 | 68 | #alias {char.reset.bag} { 69 | #class data/basic/char open; 70 | #nop 包袱; 71 | #var char-Bag {}; 72 | #class data/basic/char close; 73 | }; 74 | 75 | #alias {char.reset.sachet} { 76 | #class data/basic/char open; 77 | #nop 宝石袋; 78 | #var char-Sachet {}; 79 | #class data/basic/char close; 80 | }; 81 | 82 | #action {^你身上带著下列这些东西(负重 %1%):$} { 83 | #class {ParseItems} open; 84 | 85 | char.reset.i2; 86 | #var char-Item[currentType] {随身}; 87 | 88 | #nop 你左手拿着:大砍刀(Dakan dao); 89 | #nop 你右手拿着:青锋剑(Qingfeng sword); 90 | #action {^你{左手|右手}拿着:%%2(%%3)$} { 91 | #local hand {%%1}; 92 | #local item {%%2}; 93 | #local id {@str.ToLower{%%3}}; 94 | #var char-Wield[$hand] { 95 | {item}{$item} 96 | {id}{$id} 97 | }; 98 | } {4}; 99 | 100 | #action {^你身上穿着:$} { 101 | #var char-Item[currentType] {装备}; 102 | }; 103 | 104 | #nop 九十一两白银(Silver); 105 | #action {^{(零|一|二|三|四|五|六|七|八|九|十|百|千|万|亿)+}{两|文|张}{黄金|白银|铜板|一千两银票}(%*)$} { 106 | #local amount {@math.ParseCN{%%1}}; 107 | #local unit {%%3}; 108 | #local item {%%4}; 109 | #local id {@str.ToLower{%%5}}; 110 | 111 | #var {char-Item[$item]} { 112 | {id}{@str.ToLower{$id}} 113 | {amount}{@math.ParseCN{$amount}} 114 | }; 115 | } {4}; 116 | 117 | #local 武器等级 {沉水|赤焰|荡寇|彗月|照夜|追日|断阙|刑天}; 118 | #local 武器种类 {剑|刀|杖|鞭|斧|枪|锤|戟|匕|针|萧|钩}; 119 | 120 | #local 装备等级 {机杼|苍野|青幽|百战|鬼烈|巨灵|深罡|九日}; 121 | #local 装备种类 {铠甲|靴|袍|手套|盔|盾|披风|腰带|护腕}; 122 | 123 | #local 饰品等级 {恶来|玄狐|洛神|盘瓠|儵忽|帝江|烛阴|盘古}; 124 | #local 饰品种类 {戒指|项链}; 125 | 126 | #local 职业等级 {(..)+}; 127 | #local 职业部位 {盔|护腕|披风|护手|袍|腰带|盾|靴|项链|戒指|(板|布|鳞|皮)甲|面具|护心|护肩|腿甲}; 128 | 129 | #local 汉字数词 {(?:零|一|二|三|四|五|六|七|八|九|十|百|千|万|亿)+}; 130 | #local 汉字量词 {(?:本|块|碗|件|册|柄|把|根|枚|个|粒|味|0)}; 131 | #local 词缀修饰 {(?:[^ ][^ ])+}; 132 | 133 | #nop 物品; 134 | #action {^{?:(${汉字数词})(${汉字量词})|}%S(%*)$} { 135 | #local amount {@math.ParseCN{%%1}}; 136 | #local item {%%3}; 137 | #local id {@str.ToLower{%%4}}; 138 | #if { $amount == 0 } { 139 | #local amount {1}; 140 | }; 141 | #var {char-Item[$item]} { 142 | {id}{$id} 143 | {amount}{$amount} 144 | }; 145 | } {4}; 146 | 147 | #nop 职业装和随机装; 148 | #nop 二件玄铁之护手(Gauntlet); 149 | #nop 玄铁之板甲(Armor); 150 | #nop 天兵华彩 巨灵之手套(Hands); 151 | #action {^{((零|一|二|三|四|五|六|七|八|九|十|百|千|万|亿)+件)?}{(([^ ]+)[ ])?}%S之%S(%S)$} { 152 | #local amount {%%3}; 153 | #local title {%%6}; 154 | #local rank {%%7}; 155 | #local item {%%8}; 156 | #local id {@str.ToLower{%%9}}; 157 | 158 | #if { "${char-Item[currentType]}" == "装备" } { 159 | #return; 160 | }; 161 | 162 | #if { "$amount" == "" } { #format amount {%s} {一} }; 163 | #format amount {%s} {@math.ParseCN{$amount}}; 164 | 165 | #if { "${char-Equip[$id]}" != "" } { 166 | #math amount { ${char-Equip[$id]} + $amount }; 167 | }; 168 | 169 | #var {char-Equip[$id]} {$amount}; 170 | } {4}; 171 | 172 | #nop 二把竹剑(Zhujian); 173 | #action {^{(零|一|二|三|四|五|六|七|八|九|十|百|千|万|亿)+}{${汉字量词}}%S(%*)$} { 174 | #local amount {@math.ParseCN{%%1}}; 175 | #local unit {%%3}; 176 | #local item {%%4}; 177 | #local id {@str.ToLower{%%5}}; 178 | #var {char-Item[$item]} { 179 | {id}{$id} 180 | {amount}{$amount} 181 | }; 182 | }; 183 | 184 | #nop 大砍刀(Dakan dao); 185 | #nop 青锋剑(Qingfeng sword); 186 | #action {^%%1(%%2)$} { 187 | #local item {%%1}; 188 | #local id {@str.ToLower{%%2}}; 189 | #if { "${char-Item[currentType]}" == "装备" } { 190 | #var {char-Wear[$item]} {{id}{$id}}; 191 | }; 192 | #elseif { "${char-Item[currentType]}" == "随身" } { 193 | #var {char-Item[$item]} {{id}{$id}}; 194 | }; 195 | #elseif { "${char-Item[currentType]}" == "包袱" } { 196 | #var {char-Bag[$item]} {{id}{$id}}; 197 | }; 198 | #elseif { "${char-Item[currentType]}" == "宝石袋" } { 199 | #var {char-Sachet[$item]} {{id}{$id}}; 200 | }; 201 | } {9}; 202 | 203 | #delay 3 { 204 | #unvar char-Item[currentType]; 205 | #class {ParseItems} kill; 206 | }; 207 | 208 | #class {ParseItems} close; 209 | }; 210 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/char/jifa.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | #var basic_char_jifa[META] { 4 | {NAME} {武功激发检查} 5 | {DESC} {解析 jifa 命令的输出结果,并写入到 char[Skills]} 6 | {AUTHOR} {担子炮} 7 | {NOTE} {解析结果并不会覆盖 skills 原有数据,只是增加了 skill 之间的激发关系} 8 | {CONFIG} { 9 | } 10 | }; 11 | 12 | /* 13 | ╭───基本功夫──────┬─────────────┬─────────────╮ 14 | │内功 (force) │八荒六合唯我独尊功 │有效等级: 420 │ 15 | │躲闪 (dodge) │月影舞步 │有效等级: 769 │ 16 | │招架 (parry) │天山六阳掌 │有效等级: 419 │ 17 | ├───常用兵器──────┼─────────────┼─────────────┤ 18 | │剑法 (sword) │天羽奇剑 │有效等级: 419 │ 19 | ├───常用拳脚──────┼─────────────┼─────────────┤ 20 | │手法 (hand) │天山折梅手[互备] │有效等级: 419 │ 21 | │掌法 (strike) │天山六阳掌[互备] │有效等级: 419 │ 22 | ├───其他功夫──────┼─────────────┼─────────────┤ 23 | │暗器 (throwing) │天山六阳掌 │有效等级: 419 │ 24 | ╰─────────────┴─────────────┴────北大侠客行────╯ 25 | */ 26 | 27 | load-lib event; 28 | 29 | event.Define {char/jifa} {无参} {$MODULE} {已经获取到 jifa/enable 命令输出结果,并更新 char[Skills]。}; 30 | 31 | #func {basic_char_jifa.Init} { 32 | #return true; 33 | }; 34 | 35 | #alias {jifa} {char.Jifa}; 36 | #alias {enable} {char.Jifa}; 37 | 38 | #alias {char.Jifa} { 39 | #local args {%0}; 40 | #if { "$args" != "" } { 41 | xtt.Send {jifa $args}; 42 | #return; 43 | }; 44 | 45 | #class char.Jifa open; 46 | 47 | #action {^╭───基本功夫─{(─|┬)*}───╮$} { 48 | #local skillName {}; 49 | #foreach {*char[Skills][]} {skillName} { 50 | #unvar char[Skills][$skillName][jifa-to]; 51 | }; 52 | 53 | char.jifa.parse.output; 54 | }; 55 | 56 | #class char.Jifa close; 57 | 58 | xtt.Send {jifa}; 59 | }; 60 | 61 | #alias {char.jifa.parse.output} { 62 | #class char.Jifa open; 63 | 64 | #var char-jifa-type {基本功夫}; 65 | 66 | #action {{*UTF8}{?:^}├───{\p{Han}{4}}──{(─|┼)*}─┤{|ID=char/jifa}$} { 67 | #var char-jifa-type {%%1}; 68 | }; 69 | 70 | #action {^│%S (%*) %s│%S{|\[互备\]} %s│有效等级:%s %d %s │$} { 71 | #local baseName {基本%%1}; 72 | #if { "$baseName" == "基本躲闪" } { 73 | #local baseName {基本轻功}; 74 | }; 75 | #local skillName {%%4}; 76 | #local jifaLevel {%%8}; 77 | #if { &char[Skills][$baseName][] > 0 && "$skillName" != "无" } { 78 | #var char[Skills][$baseName][jifa-to] {$skillName}; 79 | #var char[Skills][$baseName][jifa-level] {$jifaLevel}; 80 | #var char[Skills][$baseName][jifa-type] {$char-jifa-type}; 81 | }; 82 | #if { &char[Skills][$skillName][] > 0 } { 83 | #if { "$char[Skills][$skillName][jifa-to]" == "" } { 84 | #var char[Skills][$skillName][jifa-to] {$baseName}; 85 | }; 86 | #else { 87 | #cat char[Skills][$skillName][jifa-to] {;$baseName}; 88 | }; 89 | #var char[Skills][$skillName][jifa-level] {$jifaLevel}; 90 | }; 91 | }; 92 | 93 | #action {^╰───{(┴|─)*}──%S────╯{|ID=char/jifa}$} { 94 | char.jifa.done; 95 | }; 96 | 97 | #alias {char.jifa.done} { 98 | #class char.Jifa kill; 99 | event.UnHandle {GA} {jifa.parser}; 100 | event.Emit {char/jifa}; 101 | }; 102 | 103 | event.HandleOnce {GA} {jifa.parser} {char/jifa} {char.jifa.done}; 104 | 105 | #class char.Jifa close; 106 | }; 107 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/char/sachet.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分 5 | =========== 6 | PaoTin++ © 2020~2024 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | =========== 9 | */ 10 | 11 | /* 12 | 宝石囊的名字现在有好几种版本:钵盂(僧侣)、布袋(丐帮)、人偶(五毒教)、锦囊(其他门派) 13 | 以上功能都一样,区别在于名字和 ID 不同。 14 | 15 | 另外,jiancha 命令现在要求必须带 ID 才能看到宝石内容,否则只能看到数量,因此实际上存在带参数 16 | 和不带参数两个版本。 17 | 18 | 格式分别如下: 19 | 不带参数: 20 | 钵盂[141/141] 21 | 带参数: 22 | 钵盂(Bo yu) [141/141] 23 | 这是一只紫金色的钵盂,在出家人手中很常见。 24 | ╭───五行宝石──┬─────────┬─────────┬──────────╮ 25 | │[J4A]风☆精金*1 │[M5B]雷★木灵*17 │[B7A]泽☆玄冰*1 │[Y3B]水★炎晶*2 │ 26 | │[J3A]水☆精金*2 │[M4B]风★木灵*48 │[B4A]风☆玄冰*4 │[Y2B]山★炎晶*1 │ 27 | │[J2A]山☆精金*2 │[M3B]水★木灵*8 │[B3A]水☆玄冰*34 │[Y1B]地★炎晶*1 │ 28 | │[J1A]地☆精金*1 │[M2B]山★木灵*2 │[B2A]山☆玄冰*1 │ │ 29 | │ │[M1B]地★木灵*4 │ │ │ 30 | ├───日月及中性五行宝石───────┴─────────┴──────────┤ 31 | │[S4C]风◎玉髓*3 │ 32 | │[S3C]水◎玉髓*1 │ 33 | │[S2C]山◎玉髓*3 │ 34 | ├───四灵宝石──────┬─────────────┬────────────┤ 35 | │[G1B]地★神龙骨*1 │[Q4B]风★麒麟角*1 │[X2B]山★玄武甲*1 │ 36 | │ │[Q1A]地☆麒麟角*1 │[X1B]地★玄武甲*1 │ 37 | ╰─────────────┴─────────────┴───北大侠客行────╯ 38 | 39 | 对玩家来说,jiancha 后面带不同 ID 颇有不便,所以现在改成统一 jiancha gem 表示查看详情。 40 | */ 41 | 42 | #list char[锦囊] create {}; 43 | 44 | #alias {jiancha} {char.sachet.jiancha}; 45 | 46 | #alias {char.sachet.jiancha} { 47 | #local id {%0}; 48 | 49 | #if { "$id" == "" } { 50 | xtt.Send {jiancha}; 51 | #return; 52 | }; 53 | 54 | #if { "$id" == "gem" } { 55 | #switch {"$char[档案][门派]"} { 56 | #case {"{少林派|峨嵋派|天龙寺|大轮寺}"} {#local id {bo yu}}; 57 | #case {"五毒教"} {#local id {ren ou}}; 58 | #case {"丐帮"} {#local id {baoshi dai}}; 59 | #default {#local id {jin nang}}; 60 | }; 61 | }; 62 | 63 | #list char[锦囊] create {}; 64 | 65 | #line oneshot #action {^{人偶|布袋|钵盂|锦囊}(%*) [%*/%*]{|ID=char.sachet}$} { 66 | char.sachet.parse.output; 67 | }; 68 | 69 | xtt.Send {jiancha $id}; 70 | }; 71 | 72 | #alias {char.sachet.parse.output} { 73 | #class char-parse-sachet open; 74 | 75 | #local level {地|山|水|风|雷|火|泽|天}; 76 | #local type {☆|★|◎}; 77 | #local wuxing {精金|木灵|玄冰|炎晶|玉髓}; 78 | #local beast {玄武甲|神龙骨|凤凰羽|麒麟角}; 79 | #local special {日魂|月魄}; 80 | #local pattern {\(\s*(\d+)\)($level)($type)($wuxing|$beast|$special)\*(\d+)\s+}; 81 | 82 | #action {^├───%*宝石───────┴─────────┴──────────┤$} { 83 | #nop; 84 | }; 85 | 86 | #action {^│%*│$} { 87 | #local line {%%1}; 88 | #replace line {│} {;}; 89 | #local item {}; 90 | #foreach {$line} {item} { 91 | #if { "$item" == "" } { 92 | #continue; 93 | }; 94 | #nop {[M5B]雷★木灵*30}; 95 | #local item {@__char_sachet_parse__{{$item}}}; 96 | #var char[锦囊] {$char[锦囊]{$item[序号]}{$item}}; 97 | }; 98 | }; 99 | 100 | #action {^╰──{(─|┴)+}─%S────╯{|ID=char.sachet}$} { 101 | #class char-parse-sachet kill; 102 | }; 103 | 104 | event.HandleOnce GA {char.sachet} {char} { 105 | #class char-parse-sachet kill; 106 | okLog 锦囊数据已更新。; 107 | #nop 吞参数专用,不要删除本行,也不要在末尾加分号或是别的语句 108 | }; 109 | 110 | #class char-parse-sachet close; 111 | }; 112 | 113 | #func {__char_sachet_parse__} { 114 | #local item {%1}; 115 | 116 | #replace {item} {[%S]%*{☆|★|◎}%**%d} { 117 | {序号}{&1} 118 | {等级}{&2} 119 | {阴阳}{&3} 120 | {种类}{&4} 121 | {数量}{&5} 122 | }; 123 | 124 | #return {$item}; 125 | }; 126 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/char/special.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分 5 | =========== 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | =========== 9 | */ 10 | 11 | #var basic_char_special[META] { 12 | {NAME} {特技数据管理} 13 | {DESC} {全生命周期维护 special/sp 命令结果。} 14 | {AUTHOR} {担子炮} 15 | {NOTE} {本文件属于 PaoTin++ 的一部分} 16 | }; 17 | 18 | VAR {角色特技列表,表格} {char.Special} {}; 19 | 20 | #func {basic_char_special.Init} { 21 | #delay {char.special.Init} {sp} 1; 22 | 23 | #return true; 24 | }; 25 | 26 | #alias {char.Special} { 27 | #local gag {%1}; 28 | #local args {%2}; 29 | 30 | #if { "$args" != "" } { 31 | xtt.Send {special $args}; 32 | #return; 33 | }; 34 | 35 | #class char.Special open; 36 | 37 | #action {^你现在会以下这些特技:{|ID=special/check}$} { 38 | #class char.Special open; 39 | #var char.Special {}; 40 | #action {^{\*| }%S(%S) %S{|ID=special/check}$} { 41 | #local enable {%%%1}; 42 | #local name {%%%2}; 43 | #local id {%%%3}; 44 | #local level {@str.Len{%%%4}}; 45 | 46 | #if { "$enable" == "*" } { 47 | #local enable {true}; 48 | }; 49 | #else { 50 | #local enable {false}; 51 | }; 52 | 53 | #var char.Special[$name] { 54 | {id}{$id} 55 | {name}{$name} 56 | {enable}{$enable} 57 | {level}{$level} 58 | }; 59 | }; 60 | #action {^你共拥有特技积分%d点,最多可以同时激发%d个特技。{|ID=special/check}$} { 61 | #delay 0 {okLog 角色特技数据已记录。}; 62 | #if { "%1" == "gag" } { 63 | #line gag; 64 | }; 65 | #class char.Special kill; 66 | }; 67 | #class char.Special close; 68 | }; 69 | 70 | #if { "$gag" == "gag" } { 71 | #gag {^%*{|ID=char/special}$} 1; 72 | }; 73 | 74 | #class char.Special close; 75 | 76 | xtt.Send {special}; 77 | }; 78 | 79 | #alias {special} {char.Special gag {%0}}; 80 | #alias {sp} {char.Special nogag {%0}}; 81 | 82 | #action {^你把%*加入了你的激发特技列表。$E} { 83 | #local name {%1}; 84 | #if { "$char.Special[$name]" == "" } { 85 | char.Special gag; 86 | }; 87 | #else { 88 | #var char.Special[$name][enable] {true}; 89 | }; 90 | }; 91 | 92 | #action {^你把%*从你的激发特技列表中移除。$E} { 93 | #if { "$char.Special[%1]" == "" } { 94 | char.Special gag; 95 | }; 96 | #else { 97 | #var char.Special[$name][enable] {false}; 98 | }; 99 | }; 100 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/cmds/finger.tin: -------------------------------------------------------------------------------- 1 | #var gOnlineIDs {}; 2 | 3 | #alias {finger} { 4 | #class finger-parse open; 5 | 6 | #var finger-state {}; 7 | 8 | #action {^《北大侠客行》$} {#nop}; 9 | #action {^姓名%s帐号%s发呆$} {#nop}; 10 | 11 | #action {^{(—)+}$} { 12 | #if { "${finger-state}" == "wait-end" } { 13 | #class finger-parse kill; 14 | }; 15 | }; 16 | 17 | #action {^%+1..S%+1..s%+1..S%+1..s%+1..ds%s$} { 18 | #if { "%%3" == "{[a-z]+}" } { 19 | #if { "${finger-state}" == "" } { 20 | #var finger-state {wait-end}; 21 | }; 22 | #var gOnlineIDs[%%3] {%%1}; 23 | }; 24 | }; 25 | 26 | #class finger-parse close; 27 | 28 | xtt.Send {finger}; 29 | }; 30 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/cmds/who.tin: -------------------------------------------------------------------------------- 1 | #var gOnlineUsers {}; 2 | 3 | #alias {who-l} { 4 | #class who-parse open; 5 | 6 | #action {^【%*】%*(%w)$} { 7 | #local obj {@ParseTitle{%%0}}; 8 | #if { "$obj[id]" == "" } { 9 | #echo {解析失败: %%0}; 10 | }; 11 | #else { 12 | #var gOnlineUsers[$obj[id]] { 13 | $gOnlineUsers[$obj[id]] 14 | $obj 15 | }; 16 | }; 17 | }; 18 | 19 | who-handle-end; 20 | 21 | #class who-parse close; 22 | 23 | xtt.Send {who -l}; 24 | }; 25 | 26 | #alias {who-pro-l} { 27 | #class who-parse open; 28 | 29 | #action {^【%*】%*(%w)$} { 30 | #local obj {@ParseTitle{%%0}}; 31 | #if { "$obj[id]" == "" } { 32 | #echo {解析失败: %%0}; 33 | }; 34 | #else { 35 | #local obj[pro] {$obj[rank]}; 36 | #unlocal obj[rank]; 37 | #var gOnlineUsers[$obj[id]] { 38 | $gOnlineUsers[$obj[id]] 39 | $obj 40 | }; 41 | }; 42 | }; 43 | 44 | who-handle-end; 45 | 46 | #class who-parse close; 47 | 48 | xtt.Send {who -pro -l}; 49 | }; 50 | 51 | #alias {who-all} { 52 | #class who-parse open; 53 | 54 | #action {^%S(在线%d人)(活跃 %*%)$} { 55 | #echo {%s} {LINE: %%0}; 56 | }; 57 | 58 | #action {^%S(%w){(\s+\S+\(\w+\))*}%s$} { 59 | #echo {%s} {LINE: %%0}; 60 | }; 61 | 62 | who-handle-end; 63 | 64 | #class who-parse close; 65 | 66 | xtt.Send {who}; 67 | }; 68 | 69 | #alias {who-handle-end} { 70 | #action {^{|共列出 1 位玩家。}系统负担:%S cmds/s, %S comp lines/s。$} { 71 | #class who-parse kill; 72 | }; 73 | 74 | #action {^请用help who查看指令格式。$} { 75 | #class who-parse kill; 76 | }; 77 | 78 | #action {^等等,系统喘气中......$} { 79 | #class who-parse kill; 80 | }; 81 | }; 82 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/login.extra.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:几个常用的别名 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #alias {login.auto-login} { 14 | #class login.auto-login open; 15 | 16 | #delay online.WatchDog {#zap} {180}; 17 | 18 | #config {IAC GA} {OFF}; 19 | 20 | #nop 如果角色设置了 GMCP 支持,则积极回应服务器的 GMCP 协商请求。; 21 | #if { "$user[GMCP]" == "true" } { 22 | load-lib gmcp; 23 | gmcp.Enable; 24 | }; 25 | 26 | #if { "$session[UTF8]" == "true" } { 27 | #config {charset} {UTF-8}; 28 | }; 29 | #else { 30 | #config {charset} {GBK1TOUTF8}; 31 | }; 32 | 33 | #line oneshot #action {^北大侠客行已经执行了%*。$} { 34 | #local uptime {@time.ParseDoC{%%1}}; 35 | }; 36 | 37 | #line oneshot #action {^Input 1 for GBK, 2 for UTF8, 3 for BIG5$} { 38 | #nop; 39 | }; 40 | 41 | #line oneshot #action {^您的英文名字(要注册新人物请输入new。):$} { 42 | #if { "$session[UTF8]" == "false" } { 43 | #delay 0 {xtt.Send {2}}; 44 | }; 45 | 46 | #config {charset} {UTF-8}; 47 | 48 | #if { "$login[user][id]" != "" } { 49 | xtt.Answer {$login[user][id]}; 50 | }; 51 | #else { 52 | #line oneshot #macro {\n} { 53 | #cursor get {login[user][id]}; 54 | #cursor clear; 55 | #if { "$login[user][id]" == "#%*" } { 56 | #var login[user][id] {}; 57 | }; 58 | #if { "$login[user][id]" != "" } { 59 | xtt.Answer {$login[user][id]}; 60 | }; 61 | }; 62 | }; 63 | }; 64 | 65 | #if { @isTrue{$login[user][manual]} } { 66 | #delay {online.WatchDog} {#zap} {600}; 67 | }; 68 | 69 | #line oneshot #action {^此ID档案已存在,请输入密码:$} { 70 | #if { "$login[user][passwd]" != "" } { 71 | xtt.Answer {$login[user][passwd]}; 72 | #delay {login.check} {look} 1; 73 | }; 74 | #else { 75 | #cursor flag echo on; 76 | #line oneshot #macro {\n} { 77 | #cursor get {login[user][passwd]}; 78 | #cursor clear; 79 | #if { "$login[user][passwd]" != "" } { 80 | xtt.Answer {$login[user][passwd]}; 81 | }; 82 | }; 83 | }; 84 | }; 85 | 86 | #line oneshot #action {^如果同意请用agree %*命令签署确认这些条款。$} { 87 | #local args {@str.Trim{%%1}}; 88 | #info environ save; 89 | #if { @isTrue{$info[ENVIRON][PKUXKX_AUTO_AGREE]} } { 90 | xtt.Send {agree $args}; 91 | }; 92 | #else { 93 | #undelay {login.login-success}; 94 | #undelay {login.check}; 95 | warnLog 请输入 agree $args 命令以继续游戏。; 96 | }; 97 | }; 98 | 99 | #line oneshot #action {^感谢您同意北侠玩家要约,现在可以正常进入游戏。$} { 100 | #delay {online.WatchDog} {#zap} {180}; 101 | }; 102 | 103 | #line oneshot #action {^%s欢迎来到北大侠客行!%s$} { 104 | login.login-success 重新登录; 105 | }; 106 | 107 | #line oneshot #action {^%s目前权限:(%*)%s$} { 108 | #delay login.login-success {login.login-success 重新登录} 3; 109 | }; 110 | 111 | #line oneshot #action {^您要将另一个连线中的相同人物赶出去,取而代之吗?(y/n)$} { 112 | xtt.Answer y; 113 | }; 114 | 115 | #line oneshot #action {^重新连线完毕。$} { 116 | login.login-success 断线重连; 117 | }; 118 | 119 | #class login.auto-login close; 120 | }; 121 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/map/__init__.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | ///=== { 10 | ///// 地图系统 11 | ///// }; 12 | 13 | #var basic_map[META] { 14 | {NAME} {地图系统} 15 | {DESC} {地图系统} 16 | {AUTHOR} {担子炮} 17 | }; 18 | 19 | load-lib event; 20 | load-lib sync; 21 | 22 | event.Define {map/walk/continue} {无参} {$MODULE} {走路机器人结束运行时,可以发射本事件以驱动后续动作继续运行。}; 23 | event.Define {map/walk/failed} {无参} {$MODULE} {走路机器人运行失败时,可以发射本事件以通知调用方。}; 24 | event.Define {map/init} {无参} {$MODULE} {map 模块开始初始化。}; 25 | 26 | load-file mud/pkuxkx/plugins/basic/map/room.tin; 27 | load-file mud/pkuxkx/plugins/basic/map/dungeon.tin; 28 | load-file mud/pkuxkx/plugins/basic/map/gmcp.tin; 29 | load-file mud/pkuxkx/plugins/basic/map/area.tin; 30 | load-file mud/pkuxkx/plugins/basic/map/node.tin; 31 | load-file mud/pkuxkx/plugins/basic/map/xiaoyao.tin; 32 | load-file mud/pkuxkx/plugins/basic/map/helper.tin; 33 | load-file mud/pkuxkx/plugins/basic/map/tab.tin; 34 | 35 | #func {basic_map.Init} { 36 | event.Emit map/init; 37 | set localmap; 38 | set area_detail; 39 | #return true; 40 | }; 41 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/map/area.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分 5 | =========== 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | =========== 9 | */ 10 | 11 | load-module bot/pp; 12 | 13 | VAR {全局区域对照表} {map.area.dict} {}; 14 | 15 | #nop Careless(随便查查)、Careful(仔细查查)、CarefulOnce(仔细查一次)三选一; 16 | VAR {定位模式} map.Locate.mode {Careless}; 17 | 18 | event.Define {map/GotArea} {无参} {$MODULE} {已经获取到区域信息,并更新到 gMapRoom 全局变量。}; 19 | event.Define {map/GotLocalmaps} {无参} {$MODULE} {已经解析 localmaps 命令。}; 20 | 21 | event.HandleOnce {map/init} {map/area} {map} {map.area.Init}; 22 | 23 | #alias {map.area.Init} { 24 | storage.Load {map-area} {map.area.dict}; 25 | event.Handle {map/GotRoomInfo} {map/area} {map} {map.GetArea}; 26 | }; 27 | 28 | #alias {map.GetArea} { 29 | #if { "$gMapRoom[area][RESOLVED]" != "" } {#return}; 30 | #if { "$map.Locate.mode" == "Careless" } {#return}; 31 | #if { "$gMapRoom[direction]" != "here" } {#return}; 32 | #if { @ga.IsUnderway{} } {#return}; 33 | 34 | #if { "$map.Locate.mode" == "CarefulOnce" } { 35 | #var map.Locate.mode {Careless}; 36 | }; 37 | 38 | event.HandleOnce {map/GotNodeInfo} {map/area} {map} {map.area.check walk-done}; 39 | map.GetNodeInfo; 40 | }; 41 | 42 | #alias {map.area.check} { 43 | #local stage {%1}; 44 | 45 | #local area {@map.resolveArea{$gMapRoom[area]}}; 46 | #if { "$area" == "" } { 47 | #switch {"$stage"} { 48 | #case {"walk-done"} { 49 | event.HandleOnce {map/GotLocalmaps} {map/area} {map} {map.area.check localmaps-done}; 50 | localmaps; 51 | }; 52 | #case {"localmaps-done"} { 53 | event.HandleOnce {pp/Response} {map/area} {map} {map.GetArea.pp.done}; 54 | pp $user[id]; 55 | #delay map.GetArea.pp {map.area.check pp-timeout} {30}; 56 | }; 57 | #case {"pp-timeoout"} { 58 | errLog 竭尽全力也无法获得区域信息。我这是到了什么地方?; 59 | event.UnHandle {pp/Response} {map/area} {map}; 60 | }; 61 | }; 62 | #return; 63 | }; 64 | 65 | #var gMapRoom[area][RESOLVED] {$area}; 66 | okLog 调查完毕,这里是 $gMapRoom[area][RESOLVED]的$gMapRoom[name]; 67 | event.DelayEmit map/GotArea; 68 | }; 69 | 70 | #alias {map.GetArea.pp.done} { 71 | #undelay map.GetArea.pp; 72 | #local ppInfo {$gPPResponse[$user[id]]}; 73 | #if { "$ppInfo" == "" } { 74 | errLog PP 机器人应答出错,请检查设置。; 75 | #return; 76 | }; 77 | 78 | #if { "$ppInfo[room]" != "$gMapRoom[name]" } { 79 | warnLog PP 期间角色发生了移动。定位失效。; 80 | #return; 81 | }; 82 | 83 | #var gMapRoom[area][PP] {$ppInfo[area]}; 84 | map.area.check; 85 | }; 86 | 87 | #alias {map.Localmaps} { 88 | #local gag {%1}; 89 | #local args {%2}; 90 | 91 | #if { "$args" != "" } { 92 | xtt.Send {localmaps $args}; 93 | #return; 94 | }; 95 | 96 | #var gMapRoom[area][LMAP] {}; 97 | 98 | #class map.Localmaps open; 99 | 100 | #line oneshot #action {~^{\e\[0m|}%c◆%*地图%*◆%c$} { 101 | #local color {%%2}; 102 | #replace color {\e[} {}; 103 | #replace color {m$} {}; 104 | #replace color {m} {;}; 105 | #var gMapRoom[area][LMAP] {%%3}; 106 | #var gMapRoom[area][COLOR] {$color}; 107 | } {2}; 108 | 109 | #line oneshot #action {^%s%S略图%s$} { 110 | #var gMapRoom[terrain] {随机地图}; 111 | #var gMapRoom[village] {%%2}; 112 | }; 113 | 114 | #line oneshot #action {┌─{(─)*}─%*附近详图─{(─)*}─┐$} { 115 | #var gMapRoom[terrain] {随机地图}; 116 | }; 117 | 118 | #if { "$gag" == "gag" } { 119 | #action {^%*{|ID=map/localmaps}$} {#line gag} {6}; 120 | #gag {^%*{|ID=map/localmaps}$} {1}; 121 | }; 122 | 123 | #class map.Localmaps close; 124 | 125 | xtt.Send {localmaps}; 126 | 127 | sync.Wait { 128 | #class map.Localmaps kill; 129 | event.DelayEmit map/GotLocalmaps; 130 | }; 131 | }; 132 | 133 | #alias {lm} {map.Localmaps nogag {%0}}; 134 | #alias {localmaps} {map.Localmaps gag {%0}}; 135 | 136 | #nop 根据数据源确定区域信息。数据源有三种:walk、localmaps、pp; 137 | #func {map.resolveArea} { 138 | #local source {%0}; 139 | 140 | #if { "$source[RESOLVED]" != "" } { 141 | #return {$source[RESOLVED]}; 142 | }; 143 | 144 | #if { "$source[PP]$source[WALK]$source[LMAP]" == "" } { 145 | #return {}; 146 | }; 147 | 148 | #local pp {@default{$source[PP];%*}}; 149 | #local walk {@default{$source[WALK];%*}}; 150 | #local lmap {@default{$source[LMAP];%*}}; 151 | #local pattern {$pp/$walk/$lmap}; 152 | 153 | #if { "$source[PP]" != "" } { 154 | #local key {}; 155 | #local value {{PP}{$source[PP]}}; 156 | #foreach {*map.area.dict[$source[PP]/%*/%*][]} {key} { 157 | #local value {$map.area.dict[$key]}; 158 | #unvar map.area.dict[$key]; 159 | }; 160 | #local value[WALK] {@default{$value[WALK];$source[WALK]}}; 161 | #local value[LMAP] {@default{$value[LMAP];$source[LMAP]}}; 162 | #local value[COLOR] {@default{$value[COLOR];{$source[COLOR]}}}; 163 | #local key {$source[PP]/@default{$source[WALK];UNKNOWN}/@default{$source[LMAP];UNKNOWN}}; 164 | #var map.area.dict[$key] {$value}; 165 | storage.Save {map-area} {map.area.dict}; 166 | #return {$pp}; 167 | }; 168 | 169 | #if { &map.area.dict[$pattern][] != 1 } { 170 | #return {}; 171 | }; 172 | 173 | #foreach {*map.area.dict[$pattern]} {pattern} { 174 | #return {$map.area.dict[$pattern][PP]}; 175 | }; 176 | }; 177 | 178 | #func {map.AreaColor} { 179 | #local area {%1}; 180 | #local pattern {$area/%*/%*}; 181 | 182 | #if { &map.area.dict[$pattern][] != 1 } { 183 | #return {}; 184 | }; 185 | 186 | #foreach {*map.area.dict[$pattern]} {pattern} { 187 | #return {$map.area.dict[$pattern][COLOR]}; 188 | }; 189 | }; 190 | 191 | #alias {map.Here} { 192 | #var map.Locate.mode {CarefulOnce}; 193 | look; 194 | }; 195 | 196 | #alias {ll} {map.Here} {9.0}; 197 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/map/dungeon.tin: -------------------------------------------------------------------------------- 1 | #nop 房间信息解析模块之副本管理; 2 | 3 | VAR {当前副本} {gCurrentDungeon} {主站}; 4 | VAR {上一个副本} {gPrevDungeon} {主站}; 5 | 6 | event.Define {map/EnterDungeon} {无参} {$MODULE} {进入副本,副本的名称在变量 gCurrentDungeon 中。}; 7 | event.Define {map/LeaveDungeon} {无参} {$MODULE} {离开副本,副本的名称在变量 gPrevDungeon 中。}; 8 | event.Define {map/EnterDungeonIdle} {无参} {$MODULE} {进入副本,且不再 busy,副本的名称在变量 gCurrentDungeon 中。}; 9 | event.Define {map/LeaveDungeonIdle} {无参} {$MODULE} {离开副本,且不再 busy,副本的名称在变量 gPrevDungeon 中。}; 10 | 11 | event.HandleOnce {map/init} {map/dungeon} {map} {map.Dungeon.Init}; 12 | 13 | #alias {map.Dungeon.Init} { 14 | event.Handle {map/GotRoomInfo} {map/dungeon} {map} {map.Dungeon.locate}; 15 | event.Handle {map/EnterDungeon} {map/dungeon} {map} {map.EnterDungeon.notice}; 16 | event.Handle {map/LeaveDungeon} {map/dungeon} {map} {map.LeaveDungeon.notice}; 17 | event.Handle {map/EnterDungeonIdle} {map/dungeon} {map} {map.EnterDungeon.notice idle}; 18 | event.Handle {map/LeaveDungeonIdle} {map/dungeon} {map} {map.LeaveDungeon.notice idle}; 19 | }; 20 | 21 | #alias {map.EnterDungeon.notice} { 22 | #if { "%1" == "idle" } { 23 | warnLog 你来到了「$gCurrentDungeon」,你不忙。; 24 | }; 25 | #else { 26 | warnLog 你来到了「$gCurrentDungeon」。; 27 | #tick dungeon.checkbusy {checkbusy} 1; 28 | busy.Wait { 29 | #untick dungeon.checkbusy; 30 | event.Emit map/EnterDungeonIdle; 31 | }; 32 | }; 33 | }; 34 | 35 | #alias {map.LeaveDungeon.notice} { 36 | #if { "%1" == "idle" } { 37 | okLog 你离开了「$gPrevDungeon」,你不忙。; 38 | }; 39 | #else { 40 | okLog 你离开了「$gPrevDungeon」。; 41 | #tick dungeon.checkbusy {checkbusy} 1; 42 | busy.Wait { 43 | #untick dungeon.checkbusy; 44 | event.Emit map/LeaveDungeonIdle; 45 | }; 46 | }; 47 | }; 48 | 49 | #alias {map.Dungeon.locate} { 50 | #if { "$gMapRoom[name]" == "" } { 51 | errLog 房间信息解析不正确。; 52 | #return; 53 | }; 54 | 55 | #local dungeon {主站}; 56 | 57 | #if { "$gMapRoom[name]" == "%+1..S[%+1..S]" } { 58 | #local dungeon {$gMapRoom[name]}; 59 | #replace dungeon {%+1..S[%+1..S]} {&2}; 60 | #replace dungeon {任务副本} {}; 61 | #replace gMapRoom[name] {[{[^\[]*}]} {}; 62 | #replace gMapRoom[colorName] {[{[^\[]*}]} {}; 63 | }; 64 | 65 | #if { "$dungeon" == "$gCurrentDungeon" } { 66 | #return; 67 | }; 68 | 69 | #var gPrevDungeon {$gCurrentDungeon}; 70 | #var gCurrentDungeon {$dungeon}; 71 | 72 | #if { "$dungeon" == "主站" } { 73 | event.Emit map/LeaveDungeon; 74 | }; 75 | #else { 76 | event.Emit map/EnterDungeon; 77 | }; 78 | }; 79 | 80 | #action {^准备进入%S⏳$E} { 81 | map.dungeon.change {%1}; 82 | }; 83 | 84 | #action {^准备退出%S...$E} { 85 | map.dungeon.change {%1}; 86 | }; 87 | 88 | #alias {map.dungeon.change} { 89 | #local dungeon {%1}; 90 | 91 | #nop 计有:藏经阁、鄱阳湖、万安塔、保卫襄阳、剿匪、团战、大航海等常规任务副本; 92 | #nop 及剑心居、北侠影视城、游侠等三个特别副本; 93 | #if { "$dungeon" != "{\[(.*)任务副本\]|北侠影视城|剑心居|游侠}" } { 94 | #return; 95 | }; 96 | 97 | #class map.dungeon.change open; 98 | 99 | #action {^你进入了%1。{|ID=map/dungeon}$} {map.dungeon.change.done}; 100 | #action {^你退出了%1。{|ID=map/dungeon}$} {map.dungeon.change.done}; 101 | 102 | #alias {map.dungeon.change.sync} { 103 | #class map.dungeon.change kill; 104 | #untick map.dungeon.change.sync; 105 | sync.Ignore map.dungeon.change.sync; 106 | look; 107 | }; 108 | 109 | #alias {map.dungeon.change.done} { 110 | xtt.Tick {map.dungeon.change.sync} { 111 | sync.Ignore map.dungeon.change.sync; 112 | sync.Wait {map.dungeon.change.sync} {map.dungeon.change.sync}; 113 | } 1; 114 | }; 115 | 116 | #delay {map.dungeon.change.wait} {map.dungeon.change.done} 5; 117 | 118 | #class dungeon.enter.wait close; 119 | }; 120 | 121 | #action {^请使用 leave 自己的id 来退出副本。$E} {leave $user[id]; #line gag}; 122 | 123 | #alias {enter jxj} {enter jianxinju} {5.5}; 124 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/map/gmcp.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分 5 | =========== 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | =========== 9 | */ 10 | 11 | VAR {未核销的 GMCP.Move 信息队列} {map.gmcp-move.tbc} {}; 12 | 13 | event.HandleOnce {map/init} {map/gmcp} {map} {map.GMCP.TraceMove}; 14 | 15 | #alias {map.GMCP.TraceMove} { 16 | event.Handle GMCP.Move {map/gmcp} {map} {map.GMCP.OnMove}; 17 | }; 18 | 19 | #alias {map.GMCP.StopTrace} { 20 | event.Handle GMCP.Move {map/gmcp} {map}; 21 | }; 22 | 23 | #alias {map.GMCP.OnMove} { 24 | #list map.gmcp-move.tbc add {{$gGMCP[Move]}}; 25 | }; 26 | 27 | #func {map.GMCP.Confirm} { 28 | #local move-success {@default{%1;true}}; 29 | 30 | #local cmd {@ga.ThisCmd{}}; 31 | #if { {$cmd} == {%s{l|look}{|\s+(.*)}%s} } { 32 | #replace cmd {^%s{l|look}{|\s+(.*)}%s$} { 33 | {cmd} {look} 34 | {exit} {@dir.Long{&4}} 35 | }; 36 | #return {$cmd}; 37 | }; 38 | 39 | #if { @isTrue{$move-success} } { 40 | #while { &map.gmcp-move.tbc[] > 0 && @isFalse{$map.gmcp-move.tbc[1][成功]} } { 41 | #list map.gmcp-move.tbc delete 1; 42 | }; 43 | }; 44 | 45 | #if { &map.gmcp-move.tbc[] == 0 } { 46 | errLog 发现 BUG,遇到了遗失先导 GMCP.Move 事件的行走反馈。; 47 | #return { 48 | {cmd} {$cmd} 49 | }; 50 | }; 51 | 52 | #local gmcp {$map.gmcp-move.tbc[1]}; 53 | #list map.gmcp-move.tbc delete 1; 54 | 55 | #return { 56 | {cmd} {$cmd} 57 | {gmcp} {$gmcp} 58 | }; 59 | }; 60 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/map/helper.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分 5 | =========== 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | =========== 9 | */ 10 | 11 | load-lib event; 12 | 13 | event.Define {map/walk/boat/in} {无参} {$MODULE} {已上船}; 14 | event.Define {map/walk/boat/out} {无参} {$MODULE} {即将下船}; 15 | 16 | VAR {地图脚本同步信息} map.sync.room-id {}; 17 | 18 | #alias {map.Sync} { 19 | #local prev {%1}; 20 | #local current {%2}; 21 | #local next {%3}; 22 | #local message {}; 23 | 24 | #if { "$prev$current$next" == "" } { 25 | #local message {WALK-SYNC-MESSAGE}; 26 | }; 27 | #else { 28 | #local message {F${prev}-V${current}-T$next}; 29 | #var map.sync.room-id { 30 | {prev}{$prev} 31 | {current}{$current} 32 | {next}{$next} 33 | }; 34 | }; 35 | 36 | sync.Wait { 37 | #line gag; 38 | okLog 服务器已同步。; 39 | event.DelayEmit map/walk/continue; 40 | } {$message}; 41 | }; 42 | 43 | #alias {map.GuoJiang} {map.shaogong 过江}; 44 | #alias {map.GuoHe} {map.shaogong 过河}; 45 | #alias {map.YellBoat} {map.shaogong 自助}; 46 | 47 | #alias {map.shaogong} { 48 | #local words {%1}; 49 | 50 | #class map.shaogong open; 51 | 52 | #alias {map.waitBoat} { #var map-Boat-state {waitBoat} }; 53 | 54 | #action {^只听得江面上隐隐传来:“别急嘛,这儿正忙着呐……”$} { 55 | #nop 不依赖触发了,用定时器靠谱一些; 56 | }; 57 | #action {^岸边一只渡船上的艄公说道:正等着你呢,上来吧。$} { 58 | #if { "${map-Boat-state}" == "waitBoat" } { 59 | #var map-Boat-state {enterBoat}; 60 | #untick map.yellboat; 61 | enter; 62 | event.Emit map/walk/boat/in; 63 | }; 64 | }; 65 | #action {^一叶扁舟缓缓地驶了过来,艄公将一块踏脚板搭上堤岸,以便乘客{上下。|}$} { 66 | #if { "${map-Boat-state}" == "waitBoat" } { 67 | #var map-Boat-state {enterBoat}; 68 | #untick map.yellboat; 69 | enter; 70 | event.Emit map/walk/boat/in; 71 | }; 72 | }; 73 | 74 | #nop 没有一卡通时,需要通过 ask 来手动给钱。; 75 | #action {^船老大一把拉住你,你还没付钱呢?$} { 76 | #line oneshot #action {^{他|她}是{黄|长}{河|江}边的船老大,撑渡船几十年,现在老了,只负责在岸边收钱。$} { 77 | #delay 0 {ask shao gong about 过%%%3}; 78 | }; 79 | 80 | #line oneshot #action {^%sLV%s%d%s$} {#line gag}; 81 | 82 | #delay 0 { 83 | #gag {^%*{|ID=map.shaogong}$}; 84 | look shao gong; 85 | sync.Wait {#ungag {^%*{|ID=map.shaogong}$}}; 86 | }; 87 | }; 88 | 89 | #action {^%*接过你递给的船资%S。$} {map.waitBoat}; 90 | #action {^你的车船通账上还剩%S,这一趟的船资是%S。$} {map.waitBoat}; 91 | #action {^%S道:原来是%S的人,快请上船。$} {map.waitBoat}; 92 | #action {^你吸了口气,一声“船家”,声音中正平和地远远传了出去。$} {map.waitBoat}; 93 | #action {^你鼓足中气,长啸一声:“船家!”$} {map.waitBoat}; 94 | #action {^你使出吃奶的力气喊了一声:“船家”$} {map.waitBoat}; 95 | 96 | #action {^艄公把踏脚板收起来,说了一声“坐稳喽”,竹篙一点,扁舟向{|江心驶去。}$} { 97 | #if { "${map-Boat-state}" == "enterBoat" } { 98 | #var map-Boat-state {inBoat}; 99 | #untick map.yellboat; 100 | }; 101 | }; 102 | 103 | #action {^艄公说{ |}“到啦,上岸吧”{ |},随即把一块踏脚板搭上堤岸。$} { 104 | #if { "${map-Boat-state}" == "inBoat" } { 105 | #nop 北侠 BUG 导致这条信息和下面赶下船的信息同时出现,这样会导致 out 失效。; 106 | #nop 所以做一些防御处理; 107 | #delay map-Boat-out { 108 | #var map.stepaccu 1; 109 | #unvar map-Boat-state; 110 | #undelay map-Boat-out; 111 | event.Emit map/walk/boat/out; 112 | busy.Halt {out; map.BotReturn map.shaogong}; 113 | } {0.05}; 114 | }; 115 | }; 116 | 117 | #action {^艄公要继续做生意了,所有人被赶下了渡船。$} { 118 | #if { "${map-Boat-state}" == "inBoat" } { 119 | #delay map-Boat-out { 120 | #var map.stepaccu 1; 121 | #unvar map-Boat-state; 122 | #undelay map-Boat-out; 123 | event.Emit map/walk/boat/out; 124 | busy.Halt {map.BotReturn map.shaogong}; 125 | } {0}; 126 | }; 127 | }; 128 | 129 | #tick map.yellboat {yell boat} 1; 130 | 131 | #class map.shaogong close; 132 | 133 | #if { "$words" == "自助" } { 134 | yell boat; 135 | }; 136 | #else { 137 | ask shao gong about $words; 138 | }; 139 | }; 140 | 141 | #alias {map.SpecialBoat} { 142 | #class map.SpecialBoat open; 143 | #action {^你跃上木船,船夫把木船划向海中。$} {event.Emit map/walk/boat/in}; #nop 神龙岛; 144 | #action {^你朝船夫挥了挥手便跨上岸去。$} {map.SpecialBoat.return}; #nop 神龙岛; 145 | #action {^你从踏板上走上了船。$} {event.Emit map/walk/boat/in}; #nop 桃花岛; 146 | #action {^你沿着踏板走了上去。$} {map.SpecialBoat.return}; #nop 桃花岛; 147 | #action {map.SpecialBoat.return} {event.Emit map/walk/boat/out; busy.Halt {map.BotReturn map.SpecialBoat}}; 148 | #class map.SpecialBoat close; 149 | enter boat; 150 | }; 151 | 152 | #alias {map.Ride} { 153 | #local target {%1}; 154 | 155 | #class map.Ride open; 156 | #action {^你跳上了小船,操舟向%*划去。$} {event.Emit map/walk/boat/in}; 157 | #action {^你跳上了羊皮筏子,操舟向%*划去。$} {event.Emit map/walk/boat/in}; 158 | #action {^你从小船上跳了下来,到了%*。$} {event.Emit map/walk/boat/out; busy.Halt {map.BotReturn map.Ride}}; 159 | #class map.Ride close; 160 | ride $target; 161 | }; 162 | 163 | #alias {map.BotReturn} { 164 | #local bot {%1}; 165 | #class $bot kill; 166 | event.DelayEmit {map/walk/continue} {$bot}; 167 | }; 168 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/map/node.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分 5 | =========== 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | =========== 9 | */ 10 | 11 | load-lib event; 12 | 13 | event.Define {map/GotNodeInfo} {无参} {$MODULE} {已经获取到节点信息,并更新到 gMapRoom 全局变量。}; 14 | 15 | /* 16 | ╭───扬州─────────┬─────────────╮ 17 | │目的地 │拼音名称 │ 18 | │丐帮分舵 │gaibang │ 19 | │濠州府 │haozhou │ 20 | │长江[建康府] │jiankang │ 21 | │曲阜 │qufu │ 22 | │往生堂 │shashou │ 23 | │信阳 │xinyang │ 24 | │长江[镇江] │zhenjiang │ 25 | ├──────────────┴─────────────┤ 26 | │walk [拼音名]命令使用内建路径。 │ 27 | │walk -c [拼音名]返回具体地点的路径。 │ 28 | │node 命令列出玩家自建路径。 │ 29 | ╰───────────────────北大侠客行────╯ 30 | */ 31 | 32 | ///=== { 33 | // ## map.GetNodeInfo 34 | // 通过 walk -c 命令查询当前房间的节点信息。如果当前房间不是节点,则什么也获取不到。 35 | // 否则可以获得两个信息: 36 | // 1. 当前节点的名称,如「扬州」,储存在 \$gMapRoom[node]。 37 | // 2. 当前节点所联通的节点列表,是个表格,储存在 \$gMapRoom[nodeLinks]; 38 | // }; 39 | #alias {map.GetNodeInfo} { 40 | #if { "$gMapRoom[node]" != "" } { 41 | #return; 42 | }; 43 | 44 | #class map.GetNodeInfo open; 45 | 46 | #alias {map.GetNodeInfo.done} { 47 | #var gMapRoom[area][WALK] {@default{%%1;$gMapRoom[area][WALK]}}; 48 | #line gag; 49 | #class map.GetNodeInfo kill; 50 | 51 | #if { &gMapRoom[nodeLinks][] > 0 } { 52 | #delay 0 okLog 节点信息已识别。; 53 | }; 54 | 55 | event.DelayEmit map/GotNodeInfo; 56 | }; 57 | 58 | #gag {^%*{|ID=map/getnode}$}; 59 | 60 | #action {{*UTF8}{^}╭─{(─)*}─{\p{Han}+}─{(─|┬)*}──╮{|ID=map/getnode}$} { 61 | #var gMapRoom[node] {%%4}; 62 | #var gMapRoom[nodeLinks] {}; 63 | 64 | #class map.GetNodeInfo open; 65 | 66 | #action {^│目的地%s│拼音名称%s│$} {#line gag} {4.999}; 67 | #action {^│%S%s│%S%s│{|ID=map/getnode}$} { 68 | #var {gMapRoom[nodeLinks][%%%3]} {%%%1}; 69 | }; 70 | 71 | #class map.GetNodeInfo close; 72 | }; 73 | 74 | #action {^%*内的系统内建路径出发点在:%*,请查询localmaps获得具体方位。{|ID=map/getnode}$} { 75 | #var gMapRoom[area][WALK] {%%1}; 76 | }; 77 | 78 | #action {^%*内共有一处内建玩家路径起点,在%*。{|ID=map/getnode}$} { 79 | #var gMapRoom[area][WALK] {%%1}; 80 | }; 81 | 82 | #action {^%*内共有%d处内建玩家路径起点,分别在%*。{|ID=map/getnode}$} { 83 | #var gMapRoom[area][WALK] {%%1}; 84 | }; 85 | 86 | #action {^%*当前区域没有任何内建玩家路径起点。} { 87 | #var gMapRoom[area][WALK] {%%1}; 88 | }; 89 | 90 | #class map.GetNodeInfo close; 91 | 92 | walk -c; 93 | sync.Wait {map.GetNodeInfo.done} {map/GetNodeInfo}; 94 | }; 95 | 96 | ///=== { 97 | // ## map.WalkNodes <节点列表> [<回调钩子ID>] 98 | // 执行 walk 命令,按节点列表顺序依次行走,并发射走路机器人事件。 99 | // 可选的回调钩子 ID 可以只唤醒指定的钩子,避免惊群。 100 | // 到达目的地后会自动执行 look 命令。 101 | // }; 102 | #alias {map.WalkNodes} { 103 | #class map.WalkNodes open; 104 | 105 | #var map.WalkNodes.nodes {@list.FromSlist{%1}}; 106 | #var map.WalkNodes.hook {%2}; 107 | #var map.WalkNodes.delay {3}; 108 | 109 | #local ID {|ID=map/WalkNodes}; 110 | 111 | #action {^你开始往%*方向飞奔过去……{$ID}$} { 112 | #nop prompt.Set {{nodeLinks}{<120>正在前往【%%1】,赶路中…}}; 113 | #var map.WalkNodes.delay {3}; 114 | }; 115 | 116 | #action {^频繁使用此命令会对系统造成很重负担,请稍等。{$ID}$} { 117 | #delay map.WalkNodes.retry {walk} $map.WalkNodes.delay; 118 | }; 119 | 120 | #action {^你因为种种原因停了下来,可以用walk继续进行。{$ID}$} { 121 | #switch {"$gMapRoom[name]"} { 122 | #case {"襄阳南门"} {ask shou jiang about 投军; walk}; 123 | #case {"万纶台"} {ask liang liuhe about 拜山; walk}; 124 | #case {"山路"} { 125 | #local obj {@map.Room.GetObjByID{shan xiao}}; 126 | #if { "$obj[name]" == "山魈" } { 127 | give 1 coin to shan xiao; 128 | busy.Wait walk; 129 | }; 130 | }; 131 | #default { 132 | tuna max; 133 | #delay $map.WalkNodes.delay {halt; walk}; 134 | #var map.WalkNodes.delay {1}; 135 | }; 136 | }; 137 | }; 138 | 139 | #action {^你到达了%*。{$ID}$} { 140 | #delay 0 {map.WalkNodes.walk-next}; 141 | }; 142 | 143 | #alias {map.WalkNodes.walk-next} { 144 | #if { &map.WalkNodes.nodes[] > 0 } { 145 | #local node {$map.WalkNodes.nodes[1]}; 146 | #list map.WalkNodes.nodes delete 1; 147 | #if { "$node" == "DOCK/dock" } { 148 | event.HandleOnce map/walk/continue {map.shaogong} {map} {map.WalkNodes.walk-next}; 149 | map.YellBoat; 150 | }; 151 | #elseif { "$node" == "DOCK/ride{| \S+}" } { 152 | #replace node {DOCK/ride} {}; 153 | event.HandleOnce map/walk/continue {map.Ride} {map} {map.WalkNodes.walk-next}; 154 | map.Ride $node; 155 | }; 156 | #elseif { "$node" == "PATH/{\{.*\}}" } { 157 | #replace node {PATH/{\{(.*)\}}} {&2}; 158 | xtt.SendBatch {$node}; 159 | sync.Wait {map.WalkNodes.walk-next}; 160 | }; 161 | #else { 162 | xtt.Send {walk $node}; 163 | }; 164 | }; 165 | #else { 166 | #local hook {$map.WalkNodes.hook}; 167 | #class map.WalkNodes kill; 168 | event.Emit map/walk/continue {$hook}; 169 | }; 170 | }; 171 | 172 | #class map.WalkNodes close; 173 | 174 | map.WalkNodes.walk-next; 175 | }; 176 | 177 | /* 178 | 襄阳南门 179 | 守军拦住了你的去路,大声喝到:干什么的?要想通过先问问我们守将大人! 180 | 守将给了你一块腰牌。 181 | 牧民告诉你:牧场上最近出现了一只游荡的野狼,已经吞食了很多单身的旅人,没事最好经过这里。 182 | 走路太快,你没在意脚下,被杂草绊了一下。 183 | */ 184 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/map/tab.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分 5 | =========== 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | =========== 9 | */ 10 | 11 | load-lib event; 12 | 13 | event.HandleOnce {map/init} {map/tab} {map} {map.tab-completion.init}; 14 | 15 | #alias {map.tab-completion.init} { 16 | event.Handle {map/GotRoomInfo} {map/tab} {map} {map.tab-completion}; 17 | }; 18 | 19 | #alias {map.tab-completion} { 20 | #local idx {}; 21 | 22 | #class map.tab-completion kill; 23 | #class map.tab-completion open; 24 | 25 | #foreach {*gMapRoom[objs][]} {idx} { 26 | #local obj {$gMapRoom[objs][$idx]}; 27 | #if { "$obj[id]" != "" } { 28 | #tab $obj[id]; 29 | }; 30 | }; 31 | 32 | #local item {}; 33 | #foreach {$gMapRoom[items]} {item} { 34 | #if { "$item" != "" } { 35 | #tab $item; 36 | }; 37 | }; 38 | 39 | #class map.tab-completion close; 40 | }; 41 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/basic/title.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #nop 【一代神相】江湖十大气血高手 牛肉面馆 总管事「绝对不是」天天练功 🐒 ☯️ (Marking); 10 | #nop 【 西 毒 】桃林书院 生员「喵喵喵喵喵喵喵喵喵喵喵喵喵喵喵喵」挖煤猫 🐎 ⭕️ (Zardc); 11 | #nop 朱雀 辽东大侠 胡一刀(Hu yidao) [任务发放] ◆◆ <衣钵相承>; 12 | #nop 丐帮第八代帮主 萧峰(Xiao feng) [任务发放] ◆; 13 | #nop 大善人 韩员外(Han yuanwai) [任务发放]; 14 | #nop 剑术神通「陷空洞中执九剑,能使龙池飞霹雳!」小小霹雳鼠 🗡(Xxpls); 15 | #nop 一品堂 供奉「大 爷 我 的 爱」护寺僧人 🦅 (Molia) <发呆一分钟>; 16 | #nop 大宗师 予人玫瑰 ★ 剑胆琴心★ 「语多难寄一寸相思卿安否」黑袍人 🐅 🧡 (Heipao ren); 17 | #func {ParseTitle} { 18 | #local obj {%0}; 19 | 20 | #nop 小技巧:replace 匹配之前不会将 &1~&99 清空,因此匹配可选项时会保留上一次的值。; 21 | #nop 所以这里手动清空一下; 22 | #local tmp {}; 23 | #replace {tmp} {%s%s%s%s%s%s%s%s%s%s} {}; 24 | 25 | #local rank {?:(?:【(.+?)】)?}; 26 | #local title {?:(.*?)}; 27 | #local nick {?:(?:「(.+?)」)?}; 28 | #local name {?:(\p{Han}+)}; 29 | #local emoji {?:( (?:(?:\p{So}\S*|🗡|🦅|🧡|🐅|🐒|🐎|⭕️|🐍|🙊|🐓) ?)+)?}; 30 | #local id {?:\(([A-Z][a-z']+(?: [a-z]+)*)\)}; 31 | #local status1 {?:(?: <(.*?)>)?}; 32 | #local status2 {?:(?: \[(.*?)\])?}; 33 | #local isJobNPC {?:(?: ((?:◆)+))?}; 34 | #local yibo {?:(?: <(衣钵相承)>)?}; 35 | 36 | #replace {obj} {{*UTF8}{?:^}{$rank}{$title}{$nick}{$name}{$emoji}{$id}{$emoji}{$status1}{$status2}{$isJobNPC}{$yibo}$} { 37 | {rank} {@str.Trim{&1}} 38 | {title} {@str.Trim{&2}} 39 | {nick} {@str.Trim{&3}} 40 | {name} {&4} 41 | {emoji} {@str.Trim{&5}} 42 | {id} {@str.ToLower{&6}} 43 | {status1} {&7} 44 | {status2} {&8} 45 | {isJobNPC} {&9} 46 | {yibo} {&10} 47 | }; 48 | 49 | #nop 注意 #replace 刚替换完时,得到的是字符串,最后还要再进行一次赋值,才能够将它结构化; 50 | #local obj {$obj}; 51 | 52 | #if { "$obj[name]" == "" } { 53 | #return {}; 54 | }; 55 | 56 | #if { "$obj[title]" == "☆%S%s%S☆" } { 57 | #local partyInfo {$obj[title]}; 58 | #replace partyInfo {^☆%S%s%S☆$} {{party}{&1}{partyRank}{&3}}; 59 | #local partyInfo {$partyInfo}; 60 | #local obj[title] {$partyInfo[party];$partyInfo[partyRank]}; 61 | #local obj[party] {$partyInfo[party]}; 62 | #local obj[partyRank] {$partyInfo[partyRank]}; 63 | }; 64 | 65 | #replace obj[title] {%+1..s} {;}; 66 | #replace obj[emoji] {%+1..s} {;}; 67 | #replace obj[rank] {%+1..s} {}; 68 | 69 | #local field {}; 70 | #foreach {rank;title;nick;name;emoji;id;status1;status2;isJobNPC;yibo} {field} { 71 | #if { "$obj[$field]" == "" } { 72 | #unlocal obj[$field]; 73 | }; 74 | }; 75 | 76 | #return {$obj}; 77 | }; 78 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/bot/pp.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分 5 | =========== 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | =========== 9 | */ 10 | 11 | #var bot_pp[META] { 12 | {NAME} {PP机器} 13 | {DESC} {传音搜魂,PP 查人} 14 | {AUTHOR} {担子炮} 15 | }; 16 | 17 | VAR {PP 结果,NPC 位置} gPPResponse {}; 18 | 19 | load-lib event; 20 | load-lib option; 21 | 22 | option.Define {PPBotListPrivate} {String} {私有PP大米列表} {}; 23 | option.Define {PPBotListPublic} {String} {可信的公共PP大米列表} {%*}; 24 | 25 | event.Define {pp/Response} {无参} {$MODULE} {已经获取到 PP 结果,并更新到 gPPResponse 全局变量。}; 26 | 27 | #func {bot_pp.Init} { 28 | #return true; 29 | }; 30 | 31 | ///=== { 32 | // ## pp.Find <角色 ID> 33 | // 利用公共 PP 服务查询某个 ID。 34 | // }; 35 | #alias {pp.Find} { 36 | #local id {%0}; 37 | 38 | chat.TuneOn helpme; 39 | helpme; 40 | helpme find $id; 41 | }; 42 | 43 | ///=== { 44 | // ## pp.FindQuietly <角色 ID> 45 | // 利用私有 PP 服务查询某个 ID。 46 | // }; 47 | #alias {pp.FindQuietly} { 48 | #local id {%0}; 49 | #local bot {}; 50 | 51 | #local ppList {@option.Get{PPBotListPrivate}}; 52 | #foreach {$ppList} {bot} { 53 | tell $bot find $id; 54 | }; 55 | }; 56 | 57 | ///=== { 58 | // ## pp.FindByMyself <角色 ID> 59 | // 利用自己的 PP 特技查询某个 ID。 60 | // }; 61 | #alias {pp.FindByMyself} { 62 | #local id {%0}; 63 | 64 | #class pp.FindByMyself open; 65 | 66 | #local ID {|ID=bot/pp}; 67 | 68 | #action {^不要频繁的查询,谢谢!{$ID}$} { 69 | #class pp.FindByMyself kill; 70 | #delay pp.FindByMyself.retry {pp.FindByMyself %0} 1; 71 | }; 72 | 73 | #action {^你默运玄功,将功力聚集于耳目,顿时觉得灵台一片空明。{$ID}$} { 74 | #class pp.FindByMyself kill; 75 | pp.FindByMyself.parse; 76 | #nop; 77 | }; 78 | 79 | #nop PP 对象不存在; 80 | #action {^现在没这个人。{$ID}$} { 81 | #class pp.FindByMyself kill; 82 | #showme {$char[档案][大名]($user[id])告诉你:据我所查,【%0】已不在人世了}; 83 | }; 84 | 85 | #action {^你的精力不够,无法感应别人的位置。{$ID}$} { 86 | #class pp.FindByMyself kill; 87 | errLog 精力不足,无法查询。; 88 | }; 89 | 90 | #alias {pp.FindByMyself.parse} { 91 | #class pp.FindByMyself open; 92 | 93 | #action {^你掐指一算,感觉%*(%*)现在好象在%*的%*一带活动。$} { 94 | #class pp.FindByMyself kill; 95 | #local name {%%%1}; 96 | #local qid {%%%2}; 97 | #local area {%%%3}; 98 | #local room {%%%4}; 99 | #format qid {%l} {$qid}; 100 | 101 | #showme {$char[档案][大名]($user[id])告诉你:【$name($qid)】目前在【$area的$room】,快去摁死它吧!}; 102 | }; 103 | 104 | #action {^你费了半天的力气,就是感应不出%*的位置。$} { 105 | #class pp.FindByMyself kill; 106 | }; 107 | 108 | #action {^你费了半天的力气,完全无法感应出%*的位置。$} { 109 | #class pp.FindByMyself kill; 110 | }; 111 | 112 | #action {^你费了半天的力气,完全感应不出任何状况。$} { 113 | #class pp.FindByMyself kill; 114 | }; 115 | 116 | #action {^此人已经隐姓埋名,你费了半天的力气,就是感应不出其位置。$} { 117 | #class pp.FindByMyself kill; 118 | }; 119 | 120 | #class pp.FindByMyself close; 121 | }; 122 | 123 | #class pp.FindByMyself close; 124 | 125 | perceive $id; 126 | }; 127 | 128 | #alias {pp} { 129 | #local id {%0}; 130 | 131 | #if { &char.Special[传音搜魂][] > 0 } { 132 | pp.FindByMyself $id; 133 | }; 134 | 135 | #local ppList {@option.Get{PPBotListPrivate}}; 136 | #if { "$ppList" != "" } { 137 | pp.FindQuietly $id; 138 | }; 139 | #else { 140 | pp.Find $id; 141 | }; 142 | }; 143 | 144 | #action {^你告诉%S:find $user[id]$E} {#line gag}; 145 | 146 | #action {^%S(%S)告诉你:【%S(%S)】目前在【%S的%S】,快去摁死它吧!$E} { 147 | #local tell {%2}; 148 | #local name {%3}; 149 | #local id {%4}; 150 | #local area {%5}; 151 | #local room {%6}; 152 | 153 | #local ppList {@option.Get{PPBotListPrivate};@option.Get{PPBotListPublic}}; 154 | #local pp {}; 155 | #foreach {$ppList} {pp} { 156 | #if { "$tell" == "$pp" } { 157 | #var gPPResponse[$id] { 158 | {id}{$id} 159 | {name}{$name} 160 | {area}{$area} 161 | {room}{$room} 162 | }; 163 | event.DelayEmit pp/Response; 164 | #return; 165 | }; 166 | }; 167 | 168 | warnLog 不可信的 PP 应答,简单忽略。; 169 | }; 170 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/gmcp.tin: -------------------------------------------------------------------------------- 1 | #nop GMCP 北侠支持库; 2 | #nop vim: set filetype=tt:; 3 | 4 | /* 5 | 本文件属于 PaoTin++ 的一部分。 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | */ 9 | 10 | load-lib telnet; 11 | load-lib option; 12 | load-lib event; 13 | load-lib gmcp; 14 | 15 | event.Define {GMCP.Move} {无参} {$MODULE} {接收到 GMCP 移动信息,已更新 gGMCP[Move]}; 16 | event.Define {GMCP.Status} {无参} {$MODULE} {接收到 GMCP 角色状态,已更新 gGMCP[Status]}; 17 | event.Define {GMCP.Combat} {无参} {$MODULE} {接收到 GMCP 战斗信息,已更新 gGMCP[Combat]}; 18 | event.Define {GMCP.Buff} {无参} {$MODULE} {接收到 GMCP BUFF状态,已更新 gGMCP[Buff]}; 19 | event.Define {GMCP.Message} {无参} {$MODULE} {接收到 GMCP 聊天信息,已更新 gGMCP[Message]}; 20 | 21 | #var {gmcp.key-e2c} { 22 | {Move} { 23 | {result} {成功} 24 | {dir} {出口信息} 25 | {short} {房间名} 26 | } 27 | 28 | {Status} { 29 | {name} {姓名} 30 | {max_qi} {最大气血} 31 | {qi} {气血} 32 | {jingli} {精力} 33 | {food} {食物} 34 | {eff_jing} {有效精神} 35 | {jing} {精神} 36 | {title} {头衔} 37 | {family/family_name} {门派} 38 | {combat_exp} {经验} 39 | {vigour/qi} {真气} 40 | {max_jing} {最大精神} 41 | {level} {级别} 42 | {vigour/yuan} {真元} 43 | {max_jingli} {最大精力} 44 | {neili} {内力} 45 | {water} {饮水} 46 | {eff_qi} {有效气血} 47 | {max_neili} {最大内力} 48 | {is_busy} {忙} 49 | {per} {容貌} 50 | {int} {悟性} 51 | {fighter_spirit} {战意} 52 | {is_fighting} {战斗中} 53 | {dex} {身法} 54 | {con} {根骨} 55 | {potential} {潜能} 56 | {str} {膂力} 57 | } 58 | 59 | {Combat} { 60 | {enemy_in} {敌人加入} 61 | {qi_damage} {气血伤害} 62 | {jing_wound} {精血受损} 63 | {qi_wound} {气血受损} 64 | {eff_jing_pct} {有效精血百分比} 65 | {enemy_out} {敌人退出} 66 | {eff_qi_pct} {有效气血百分比} 67 | {jing_pct} {精血百分比} 68 | {jing_damage} {精血伤害} 69 | {qi_pct} {气血比率} 70 | {name} {姓名} 71 | {perform_name} {绝招名称} 72 | {perform_cd} {CD时长} 73 | {perform_id} {绝招ID} 74 | } 75 | 76 | {Buff} { 77 | {type} {效果类型} 78 | {is_end} {效果结束} 79 | {last_inc} {效果延时} 80 | {name} {效果名称} 81 | {effects} {具体效果} 82 | {effect1} {效果1} 83 | {effect2} {效果2} 84 | {effect3} {效果3} 85 | {effect4} {效果4} 86 | {effect5} {效果5} 87 | {last_time} {持续时间} 88 | {terminated} {将中止的效果} 89 | } 90 | 91 | {Message} { 92 | {channel} {频道} 93 | {type} {信息类型} 94 | {seq} {图片编号} 95 | {no} {QQ号码} 96 | {name} {姓名} 97 | {url} {下载地址} 98 | } 99 | }; 100 | 101 | #alias {gmcp.pkuxkx.Enable} { 102 | #event {IAC SB GMCP GMCP.Status IAC SE} {gmcp.pkuxkx.OnStatus {%%1} {%%2}}; 103 | #event {IAC SB GMCP GMCP.Move IAC SE} {gmcp.pkuxkx.OnMove {%%1} {%%2}}; 104 | #event {IAC SB GMCP GMCP.Combat IAC SE} {gmcp.pkuxkx.OnCombat {%%1} {%%2}}; 105 | #event {IAC SB GMCP GMCP.Buff IAC SE} {gmcp.pkuxkx.OnBuff {%%1} {%%2}}; 106 | #event {IAC SB GMCP GMCP.Message IAC SE} {gmcp.pkuxkx.OnMessage {%%1} {%%2}}; 107 | 108 | tune gmcp status on; 109 | tune gmcp move on; 110 | tune gmcp buff on; 111 | tune gmcp combat on; 112 | tune gmcp message on; 113 | }; 114 | 115 | #alias {gmcp.pkuxkx.OnStatus} { 116 | mudLog GMCP.Status: {%2}; 117 | 118 | #line sub escapes #var gGMCP[Status] {%1}; 119 | gmcp.translate-key Status; 120 | 121 | #if { @option.IsEnable{GMCPDebug} } { 122 | #var gGMCP[Status]; 123 | }; 124 | 125 | event.Emit GMCP.Status {}; 126 | }; 127 | 128 | #alias {gmcp.pkuxkx.OnMove} { 129 | mudLog GMCP.Move: {%2}; 130 | 131 | #line sub escapes #var gGMCP[Move] {%1}; 132 | #var gGMCP[Move] {$gGMCP[Move][1]}; 133 | gmcp.translate-key Move; 134 | 135 | #list gGMCP[Move][出口信息] sort; 136 | 137 | #if { @option.IsEnable{GMCPDebug} } { 138 | #var gGMCP[Move]; 139 | }; 140 | 141 | event.Emit GMCP.Move {}; 142 | }; 143 | 144 | #alias {gmcp.pkuxkx.OnCombat} { 145 | mudLog GMCP.Combat: {%2}; 146 | 147 | #line sub escapes #var gGMCP[Combat] {%1}; 148 | #var gGMCP[Combat] {$gGMCP[Combat][1]}; 149 | gmcp.translate-key Combat; 150 | 151 | #if { @option.IsEnable{GMCPDebug} } { 152 | #var gGMCP[Combat]; 153 | }; 154 | 155 | event.Emit GMCP.Combat {}; 156 | }; 157 | 158 | #alias {gmcp.pkuxkx.OnBuff} { 159 | mudLog GMCP.Buff: {%2}; 160 | 161 | #line sub escapes #var gGMCP[Buff] {%1}; 162 | gmcp.translate-key Buff; 163 | 164 | #if { @option.IsEnable{GMCPDebug} } { 165 | #var gGMCP[Buff]; 166 | }; 167 | 168 | event.Emit GMCP.Buff {}; 169 | }; 170 | 171 | #alias {gmcp.pkuxkx.OnMessage} { 172 | mudLog GMCP.Message: {%2}; 173 | 174 | #line sub escapes #var gGMCP[Message] {%1}; 175 | gmcp.translate-key Message; 176 | 177 | #if { @option.IsEnable{GMCPDebug} } { 178 | #var gGMCP[Message]; 179 | }; 180 | 181 | event.Emit GMCP.Message {}; 182 | }; 183 | 184 | #alias {gmcp.translate-key} { 185 | #local class {%1}; 186 | 187 | #if { "$class" != "{Move|Status|Combat|Buff|Message}" } { 188 | #return; 189 | }; 190 | 191 | #local key {}; 192 | #foreach {*gGMCP[$class][]} {key} { 193 | #local new {$gmcp.key-e2c[$class][$key]}; 194 | #if { "$new" == "" } { 195 | #continue; 196 | }; 197 | #var gGMCP[$class][$new] {$gGMCP[$class][$key]}; 198 | #unvar gGMCP[$class][$key]; 199 | }; 200 | }; 201 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/lib/sync.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | ///=== { 10 | ///// sync 是一个服务器同步机制,可以确保服务器已经处理完了之前发送的命令队列。 11 | ///// 假设服务器是按照顺序处理客户端发送的命令,那么因为存在网络延迟,因此客户端 12 | ///// 可能已经发送了多个命令在网络上,但还没有到达服务器,或者服务器因为繁忙所以 13 | ///// 将其放入了命令缓冲队列尚未响应。 14 | ///// 15 | ///// 但有些时候,客户端需要明确服务器是否已经处理了之前发送的命令。此时可以使用 16 | ///// 本模块。其基本原理是,通过发送别致同步符号,并请求服务器回显该符号,以等待 17 | ///// 服务器处理完之前发送的命令。如果收到了服务器的回显,则认为命令缓冲已空,并 18 | ///// 可以进行后续操作。 19 | ///// }; 20 | 21 | #var lib_sync[META] { 22 | {NAME} {服务器同步} 23 | {DESC} {和服务器进行同步,本模块为北侠版} 24 | {AUTHOR} {担子炮} 25 | {NOTE} {} 26 | {CONFIG} {} 27 | }; 28 | 29 | ///=== { 30 | // #@ sync.UUID 31 | // 用来生成一个唯一的同步符号。 32 | // }; 33 | #func {sync.UUID} { 34 | #return {@uuid{}}; 35 | }; 36 | 37 | ///=== { 38 | // ## sync.Wait <回调代码> [<同步符号>] 39 | // 用来和服务器进行同步,参数说明如下: 40 | // 1. 回调代码 41 | // 回调代码会在与服务器同步之后执行。 42 | // 2. 同步符号(可选) 43 | // 同步符号参数用来对本次同步进行唯一标识,只能由大小写字母、数字、连字符、 44 | // 下划线、小数点、斜线构成。 45 | // 本参数为可选值,如果省略,则无法通过 sync.Ignore 取消回调 46 | // }; 47 | #alias {sync.Wait} { 48 | #local callback {%1}; 49 | #local uuid {%2}; 50 | 51 | #if { {$callback} == {} } { 52 | xtt.Usage sync.Wait 回调代码不能为空; 53 | #return; 54 | }; 55 | 56 | #if { {$uuid} == {%*{[^a-zA-Z0-9_./-]}%*} } { 57 | xtt.Usage sync.Wait 同步符号不能这么写; 58 | #return; 59 | }; 60 | 61 | #if { "$uuid" == "" } { 62 | #format uuid {%U}; 63 | }; 64 | 65 | sync.handle {$uuid} {$callback}; 66 | 67 | xtt.Send {response R:sync-$uuid}; 68 | }; 69 | 70 | ///=== { 71 | // #@ sync.Wait <回调代码> 72 | // 函数版的 sync.Wait 相比别名版的 sync.Wait,省略了同步符号参数要求, 73 | // 改由 sync 模块自行生成,并作为返回值返回给用户。 74 | // }; 75 | #func {sync.Wait} { 76 | #local callback {%1}; 77 | 78 | #if { {$callback} == {} } { 79 | xtt.Usage sync.Wait 回调代码不能为空; 80 | #return; 81 | }; 82 | 83 | #local uuid {}; 84 | #format uuid {%U}; 85 | 86 | sync.Wait {$callback} {$uuid}; 87 | 88 | #return {$uuid}; 89 | }; 90 | 91 | #nop 这里保存同步符号和回调代码的对应关系。; 92 | #var gSyncHandlers {}; 93 | 94 | #alias {sync.handle} { 95 | #local uuid {%1}; 96 | #local callback {%2}; 97 | #var {gSyncHandlers[$uuid]} {$callback}; 98 | }; 99 | 100 | ///=== { 101 | // ## sync.Ignore <同步符号> 102 | // 忽略指定的同步信息,将不会再触发回调代码。 103 | // }; 104 | #alias {sync.Ignore} { 105 | #local uuid {%1}; 106 | #unvar {gSyncHandlers[$uuid]}; 107 | }; 108 | 109 | #nop 根据服务器返回的信息,调用相应的回调代码。; 110 | #action {^系统回馈:R:sync-%*$E} { 111 | #local uuid {%1}; 112 | 113 | ga.Confirm {response R:sync-$uuid}; 114 | 115 | #local callback {$gSyncHandlers[$uuid]}; 116 | #unvar {gSyncHandlers[$uuid]}; 117 | #if { {$callback} != {} } { 118 | $callback; 119 | }; 120 | 121 | #line gag; 122 | } {4.5}; 123 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/lib/ui/move.tin: -------------------------------------------------------------------------------- 1 | #var lib_ui_move[META] { 2 | {NAME} {走来走去} 3 | {DESC} {抓取玩家走来走去的信息,放置到单独的日志} 4 | {AUTHOR} {担子炮} 5 | }; 6 | 7 | load-lib option; 8 | option.Define {GagMove} {Bool} {是否抑制玩家移动信息} {true}; 9 | 10 | #alias {move-log} { 11 | #local text {%1}; 12 | #replace {text} {,右手拿着} {/}; 13 | #replace {text} {,左手拿着} {/}; 14 | #replace {text} {,右手} {/}; 15 | #replace {text} {,左手} {/}; 16 | #replace {text} {,手持} {/}; 17 | #replace {text} {,身穿} {/}; 18 | #replace {text} {,拿着} {/}; 19 | #replace {text} {右手拿着} {/}; 20 | #replace {text} {左手拿着} {/}; 21 | #replace {text} {右手} {/}; 22 | #replace {text} {左手} {/}; 23 | #replace {text} {手持} {/}; 24 | #replace {text} {身穿} {/}; 25 | #replace {text} {拿着} {/}; 26 | #replace {text} {走了过来} {/&0}; 27 | 28 | moveLog $text; 29 | 30 | #if { @option.IsEnable{GagMove} } { 31 | #line gag; 32 | }; 33 | }; 34 | 35 | #action {^你} {#0} {9.998}; 36 | 37 | #nop 这里不要改,表现很奇怪; 38 | #var dir {((东|西)(南|北))|((东|南|西|北|上|下|里|外)(|上|下|面|边))|[1-9]}; 39 | #action {~^%*{往|向}{$dir}{离开|离去|驶去}。$CE} { move-log {%0} }{9.999}; 40 | #unvar dir; 41 | 42 | #action {~^%*本来好端端在这里的,忽然消失了,好象人间蒸发了一样。$CE} { move-log {%0} }{9.999}; 43 | #action {~^%*走过来扫了在场各人一眼,一股凛冽的杀意油然而生。$CE} { move-log {%0} }{9.999}; 44 | #action {~^%*你闻到一丝淡淡的香气,紧接着%*走了过来。$CE} { move-log {%0} }{9.997}; 45 | #action {~^%*跌跌撞撞地跑了过来,模样有些狼狈。$CE} { move-log {%0} }{9.999}; 46 | #action {~^%*如离弦之箭一般冲了过来。$CE} { move-log {%0} }{9.999}; 47 | #action {~^%*一转眼,场间早已没有了%*的身影。$CE} { move-log {%0} }{9.999}; 48 | #action {~^%*开始往%*方向飞奔过去……$CE} { move-log {%0} }{9.999}; 49 | #action {~^%*顺着%*的路径飞奔了过去。$CE} { move-log {%0} }{9.999}; 50 | #action {~^%*{走|驶}了{过来|进去|出来|进来}。$CE} { move-log {%0} }{9.999}; 51 | #action {~^%*走了过来,%*$CE} { move-log {%0} }{9.999}; 52 | #action {~^%*慢慢地离开。$CE} { move-log {%0} }{9.999}; 53 | #action {~^%*轻轻地离开了。$CE} { move-log {%0} }{9.999}; 54 | #action {~^%*不紧不慢地踱着步子离开了。$CE} { move-log {%0} }{9.999}; 55 | 56 | #action {~^%*一道身影出现在场间%*$CE} { move-log {%0} }{9.999}; 57 | #action {~^%*人影飘了过来。$CE} { move-log {%0} }{9.999}; 58 | #action {~^%*人影一闪就消失不见了。$CE} { move-log {%0} }{9.999}; 59 | #action {~^%*一条人影从这里离开。$CE} { move-log {%0} }{9.999}; 60 | 61 | #action {~^%*已经离开了这里。$CE} { move-log {%0} }{9.999}; 62 | #action {~^%*疾步走了过来%*$CE} { move-log {%0} }{9.999}; 63 | #action {~^%*快步离开%*气势也随之而散。$CE} { move-log {%0} }{9.999}; 64 | 65 | #action {~^%*一道人影轻轻地飘了过来。$CE} { move-log {%0} }{9.999}; 66 | #action {~^%*不经意间,只是人影一闪。$CE} { move-log {%0} }{9.999}; 67 | #action {~^%*杀意随着%*也离开了这里。$CE} { move-log {%0} }{9.999}; 68 | 69 | #action {~^%*将%*脱了下来。$CE} { move-log {%0} }{9.999}; 70 | #action {~^%*将锦囊%*,放回怀中。$CE} { move-log {%0} }{9.999}; 71 | #action {~^%*{穿|戴}上一%*。$CE} { move-log {%0} }{9.999}; 72 | #action {~^%*丢下一%*。$CE} { move-log {%0} }{9.999}; 73 | #action {~^%*装备%*。$CE} { move-log {%0} }{9.999}; 74 | 75 | #action {~^%*只见小木门乒地关上了。$CE} { move-log {%0} }{9.999}; 76 | 77 | #action {~^%*脸色看起来好多了。$CE} { move-log {%0} }{9.999}; 78 | #action {~^%*拿起%*咕噜噜地喝了几口%*。$CE} { move-log {%0} }{9.999}; 79 | #action {~^%*拿起%*咬了几口。$CE} { move-log {%0} }{9.999}; 80 | 81 | #action {~^%*运行真气加速自身的气血恢复。$CE} { move-log {%0} }{9.999}; 82 | #action {~^%*减缓真气运行,让气血运行恢复正常。$CE} { move-log {%0} }{9.999}; 83 | 84 | #action {~^%*从陈旧的剑鞘中拔出一把%*握在手中。$CE} { move-log {%0} }{9.999}; 85 | #action {~^%*放下手中的%*。$CE} { move-log {%0} }{9.999}; 86 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/lib/ui/prompt.extra.tin: -------------------------------------------------------------------------------- 1 | #alias {prompt.UpdateHP} { 2 | prompt.Set { 3 | {exp}{$char[HP][经验显示]} 4 | {pot}{$char[HP][潜能显示]} 5 | {battle}{$char[HP][战斗中]} 6 | {busy}{$char[HP][忙]} 7 | } 8 | }; 9 | 10 | #alias {prompt.UpdateSM} { 11 | #local effect {$char[STATUS][持续效果]}; 12 | #replace effect {秒)} {)}; 13 | 14 | prompt.Set { 15 | {yunqi}{$char[STATUS][气血恢复]} 16 | {status}{$char[STATUS][健康状态]} 17 | {persist}{$effect} 18 | } 19 | }; 20 | 21 | event.HandleOnce {user-online} {pkuxkx/online} {pkuxkx/online} { 22 | event.Handle {char/hpbrief} {prompt} {framework/online} {prompt.UpdateHP}; 23 | event.Handle {char/status} {prompt} {framework/online} {prompt.UpdateSM}; 24 | }; 25 | -------------------------------------------------------------------------------- /mud/pkuxkx/plugins/lib/xtintin/cmds.extra.tin: -------------------------------------------------------------------------------- 1 | #nop 北侠版的 xtt.SendBatch,允许一次性向服务器发送多条命令。; 2 | #alias {xtt.SendBatch} { 3 | #local cmds {%1}; 4 | #nop TODO: 允许自定义多条命令之间的分隔符。; 5 | xtt.Send {#$cmds#}; 6 | }; 7 | -------------------------------------------------------------------------------- /mud/thuxyj/framework/online.extra.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:几个常用的别名 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #alias {online.KeepAlive} {localtime}; 14 | 15 | event.HandleOnce {user-online} {thuxyj/online} {thuxyj/online} { 16 | set openmap 1; 17 | }; 18 | -------------------------------------------------------------------------------- /mud/thuxyj/plugins/basic/login.extra.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:几个常用的别名 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #alias {login.auto-login} { 14 | #class login.auto-login open; 15 | 16 | #config {IAC GA} {OFF}; 17 | 18 | #nop 如果角色设置了 GMCP 支持,则积极回应服务器的 GMCP 协商请求。; 19 | #if { "$user[GMCP]" == "true" } { 20 | load-lib gmcp; 21 | gmcp.Enable; 22 | }; 23 | 24 | #if { "$session[UTF8]" == "true" } { 25 | #config {charset} {UTF-8}; 26 | }; 27 | #else { 28 | #config {charset} {GBK1TOUTF8}; 29 | }; 30 | 31 | #line oneshot #action {^%sWelcome to Xi You Ji! Select GB or BIG5 (gb/big5):$} { 32 | #line oneshot #event {CATCH VT100 ERASE SCREEN ALL} {#0;#buffer end}; 33 | xtt.Answer gb; 34 | }; 35 | 36 | #line oneshot #action {^您是否是中小学学生或年龄更小?(yes/no)$} { 37 | #delay 0 {xtt.Send no}; 38 | }; 39 | 40 | #line oneshot #action {^您的英文名字:(新玩家请键入 new 注册)$} { 41 | #if { "$login[user][id]" != "" } { 42 | xtt.Answer {$login[user][id]}; 43 | }; 44 | #else { 45 | #line oneshot #macro {\n} { 46 | #cursor get {login[user][id]}; 47 | #cursor clear; 48 | #if { "$login[user][id]" == "#%*" } { 49 | #var login[user][id] {}; 50 | }; 51 | #if { "$login[user][id]" != "" } { 52 | xtt.Answer {$login[user][id]}; 53 | }; 54 | }; 55 | }; 56 | }; 57 | 58 | #action {^请输入相应密码:$} { 59 | #if { "$login[user][passwd]" != "" } { 60 | xtt.Answer {$login[user][passwd]}; 61 | #delay {login.check} {look} 1; 62 | }; 63 | #else { 64 | #cursor flag echo on; 65 | #line oneshot #macro {\n} { 66 | #cursor get {login[user][passwd]}; 67 | #cursor clear; 68 | #if { "$login[user][passwd]" != "" } { 69 | xtt.Answer {$login[user][passwd]}; 70 | }; 71 | }; 72 | }; 73 | }; 74 | 75 | #line oneshot #action {^您要将另一个连线中的相同人物赶出去,取而代之吗?(y/n)$} { 76 | xtt.Answer y; 77 | }; 78 | 79 | #line oneshot #action {^%s目前权限:(player)%s$} { 80 | #delay {login.login-success} {login.login-success} 1; 81 | }; 82 | 83 | #line oneshot #action {^重新连线完毕。$} { 84 | #delay {login.login-success} {login.login-success 断线重连} 0; 85 | }; 86 | 87 | #class login.auto-login close; 88 | }; 89 | -------------------------------------------------------------------------------- /mud/thuxyj/plugins/lib/sync.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | ///=== { 10 | ///// sync 是一个服务器同步机制,可以确保服务器已经处理完了之前发送的命令队列。 11 | ///// 假设服务器是按照顺序处理客户端发送的命令,那么因为存在网络延迟,因此客户端 12 | ///// 可能已经发送了多个命令在网络上,但还没有到达服务器,或者服务器因为繁忙所以 13 | ///// 将其放入了命令缓冲队列尚未响应。 14 | ///// 15 | ///// 但有些时候,客户端需要明确服务器是否已经处理了之前发送的命令。此时可以使用 16 | ///// 本模块。其基本原理是,通过发送别致同步符号,并请求服务器回显该符号,以等待 17 | ///// 服务器处理完之前发送的命令。如果收到了服务器的回显,则认为命令缓冲已空,并 18 | ///// 可以进行后续操作。 19 | ///// }; 20 | 21 | #var lib_sync[META] { 22 | {NAME} {服务器同步} 23 | {DESC} {和服务器进行同步,本模块为北侠版} 24 | {AUTHOR} {担子炮} 25 | {NOTE} {} 26 | {CONFIG} {} 27 | }; 28 | 29 | ///=== { 30 | // #@ sync.UUID 31 | // 用来生成一个唯一的同步符号。 32 | // }; 33 | #func {sync.UUID} { 34 | #return {@uuid{}}; 35 | }; 36 | 37 | ///=== { 38 | // ## sync.Wait <回调代码> [<同步符号>] 39 | // 用来和服务器进行同步,参数说明如下: 40 | // 1. 回调代码 41 | // 回调代码会在与服务器同步之后执行。 42 | // 2. 同步符号(可选) 43 | // 同步符号参数用来对本次同步进行唯一标识,只能由大小写字母、数字、连字符、 44 | // 下划线、小数点、斜线构成。 45 | // 本参数为可选值,如果省略,则无法通过 sync.Ignore 取消回调 46 | // }; 47 | #alias {sync.Wait} { 48 | #local callback {%1}; 49 | #local uuid {%2}; 50 | 51 | #if { {$callback} == {} } { 52 | xtt.Usage sync.Wait 回调代码不能为空; 53 | #return; 54 | }; 55 | 56 | #if { {$uuid} == {%*{[^a-zA-Z0-9_./-]}%*} } { 57 | xtt.Usage sync.Wait 同步符号不能这么写; 58 | #return; 59 | }; 60 | 61 | #if { "$uuid" == "" } { 62 | #format uuid {%U}; 63 | }; 64 | 65 | sync.handle {$uuid} {$callback}; 66 | 67 | xtt.Send {set public sync-$uuid}; 68 | }; 69 | 70 | ///=== { 71 | // #@ sync.Wait <回调代码> 72 | // 函数版的 sync.Wait 相比别名版的 sync.Wait,省略了同步符号参数要求, 73 | // 改由 sync 模块自行生成,并作为返回值返回给用户。 74 | // }; 75 | #func {sync.Wait} { 76 | #local callback {%1}; 77 | 78 | #if { {$callback} == {} } { 79 | xtt.Usage sync.Wait 回调代码不能为空; 80 | #return; 81 | }; 82 | 83 | #local uuid {}; 84 | #format uuid {%U}; 85 | 86 | sync.Wait {$callback} {$uuid}; 87 | 88 | #return {$uuid}; 89 | }; 90 | 91 | #nop 这里保存同步符号和回调代码的对应关系。; 92 | #var gSyncHandlers {}; 93 | 94 | #alias {sync.handle} { 95 | #local uuid {%1}; 96 | #local callback {%2}; 97 | #var {gSyncHandlers[$uuid]} {$callback}; 98 | }; 99 | 100 | ///=== { 101 | // ## sync.Ignore <同步符号> 102 | // 忽略指定的同步信息,将不会再触发回调代码。 103 | // }; 104 | #alias {sync.Ignore} { 105 | #local uuid {%1}; 106 | #unvar {gSyncHandlers[$uuid]}; 107 | }; 108 | 109 | #nop 根据服务器返回的信息,调用相应的回调代码。; 110 | #action {^设定环境变数:public = "sync-%S"$E} { 111 | #local uuid {%1}; 112 | 113 | ga.Confirm {set public sync-$uuid}; 114 | 115 | #local callback {$gSyncHandlers[$uuid]}; 116 | #unvar {gSyncHandlers[$uuid]}; 117 | #if { {$callback} != {} } { 118 | $callback; 119 | }; 120 | 121 | #line gag; 122 | }; 123 | -------------------------------------------------------------------------------- /nanorc: -------------------------------------------------------------------------------- 1 | # vim: set filetype=nanorc: 2 | 3 | set autoindent 4 | set casesensitive 5 | set boldtext 6 | set mouse 7 | set noconvert 8 | set tabsize 4 9 | set tabstospaces 10 | 11 | # PaoTin++ 语法高亮设置 12 | 13 | syntax "PaoTin" "\.(tin)$" 14 | icolor yellow "#nop .*" 15 | icolor cyan "#\<(|un)(act(|i(|o(|n)))|ali(|a(|s))|tic(|k(|e(|r)))|var(|i(|a(|b(|l(|e)))))|del(|a(|y))|gag|tab|but(|t(|o(|n)))|eve(|n(|t))|fun(|c(|t(|i(|o(|n)))))|hig(|h(|l(|i(|g(|h(|t))))))|mac(|r(|o))|pat(|h(|d(|i(|r))))|pro(|m(|p(|t)))|sub(|s(|t(|i(|t(|u(|t(|e))))))))\>" 16 | icolor cyan "#\<(echo|show(|m(|e))|cat|regex|local|replace|unlocal)\>" 17 | icolor green "#\<(if|elseif|else|break|continue|return|switch|case|default|while|foreach|loop|match|parse)\>" 18 | color magenta "[(){}]" "\[" "\]" 19 | color brightblue start="/\*" end="\*/" 20 | -------------------------------------------------------------------------------- /paotin-start: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -f /.dockerenv ]; then 4 | echo Docker 环境下不需要执行本命令,直接用 start-ui 即可启动。 5 | exit; 6 | fi 7 | 8 | TMP=$(which bash) 9 | if [ "x$TMP" = "x" ]; then 10 | echo 本程序运行前需要先安装 bash/tmux,并编译 TinTin++。 11 | echo 请参考 README.md 指引进行安装。 12 | exit 13 | fi 14 | 15 | export SHELL=$TMP 16 | 17 | TMP=$(which tmux) 18 | if [ "x$TMP" = "x" ]; then 19 | echo 本程序运行前需要先安装 bash/tmux,并编译 TinTin++。 20 | echo 请参考 README.md 指引进行安装。 21 | exit 22 | fi 23 | 24 | if [ ! -x bin/tt++ ]; then 25 | echo 本程序运行前需要先安装 bash/tmux,并编译 TinTin++。 26 | echo 请参考 README.md 指引进行安装。 27 | exit 28 | fi 29 | 30 | export HOME=$(pwd) 31 | export PATH=$HOME/bin:$PATH 32 | export TMUXCMD="tmux -S $HOME/tmux/sock -f $HOME/tmux.conf" 33 | 34 | test -f .bash_profile || ln -s profile.sh .bash_profile 35 | exec bash -l start-ui 36 | -------------------------------------------------------------------------------- /plugins/EXAMPLE.tin: -------------------------------------------------------------------------------- 1 | #nop 样板模块; 2 | 3 | /* 4 | 加载模块用 load-module EXAMPLE 命令。 5 | 如果是在命令行输入,可以用短名称 LM EXAMPLE 也是一样的效果。代码中建议用长名称。 6 | 对于纯模块而言,也可以在加载模块时提供参数: 7 | 8 | load-module EXAMPLE { {foo}{value1} {bar}{value2} } 9 | 10 | 参数会传送给 EXAMPLE[config] 变量,供模块使用。 11 | */ 12 | 13 | #nop 模块元信息描述。; 14 | #nop 如果不提供这个变量,则称为「弱模块」。; 15 | #nop 如果提供了这个变量,并设计了 EXAMPLE.Run 别名,则称为「纯模块」。纯模块会有更多功能。; 16 | #nop 模块加载器会为纯模块自动生成 EXAMPLE.Start 和 EXAMPLE.Stop 两个方法。; 17 | #var EXAMPLE[META] { 18 | {NAME} {样板模块} 19 | {DESC} {本模块用来展示 PaoTin++ 的模块加载机制和模块书写规范。} 20 | {AUTHOR} {担子炮} 21 | {NOTE} {可以在这里写模块的使用注意事项或者版权声明等。} 22 | {CONFIG} { 23 | {foo} {模块配置参数1,前面是名称,后面写配置参数的说明,给人看的。} 24 | {bar} {模块配置参数2,可以按需扩展。} 25 | } 26 | }; 27 | 28 | #nop 纯模块允许有一个 Init 函数,会在模块加载后自动调用。; 29 | #func {EXAMPLE.Init} { 30 | #nop load-module 时提供的参数最终会保存到 $EXAMPLE[config] 变量里,供插件使用。; 31 | #var EXAMPLE[config]; 32 | okLog EXAMPLE 初始化中…; 33 | 34 | #nop Init 函数必须返回 true 以表示模块成功初始化。; 35 | #nop 如果返回别的值,则模块加载将会失败,已加载的部分也会被卸载。; 36 | #return true; 37 | }; 38 | 39 | #nop 纯模块加载后默认处于禁用状态,必须调用 EXAMPLE.Start 后才会开始运行。; 40 | #nop EXAMPLE.Start 方法会调用一次 EXAMPLE.Run 方法,用户可以在这里驱动模块事件循环; 41 | #alias {EXAMPLE.Run} { 42 | #nop xxxLog 可以用来输出日志。; 43 | #nop 系统预定义的日志有 infoLog errLog okLog warnLog dbgLog mudLog; 44 | #nop 每种不同的 Log 文件会存放在单独的文件中。; 45 | #nop 你也可以自定义自己的日志,比如叫 myjobLog 也是可以的,会自动生成 myjob.log 文件。; 46 | okLog Hello, I'm a pure PaoTin++ module, My name is EXAMPLE.; 47 | 48 | #nop xtt.Tick 类似于 #tick,但是会立即运行一次定时器。; 49 | xtt.Tick {EXAMPLE.timer} {EXAMPLE.Test} 10; 50 | }; 51 | 52 | #nop 纯模块也可以通过 EXAMPLE.Stop 来停止运行。停止运行的纯模块就像是尚未加载一样,不会对系统其它部分产生任何影响。; 53 | #nop 你也可以用 EXAMPLE.Start 来再次运行本模块,系统会继续之前 Stop 时保存的状态,包括变量和触发都不会丢失。; 54 | #alias {EXAMPLE.Pause} { 55 | okLog EXAMPLE 暂停运行。; 56 | }; 57 | 58 | #nop 纯模块也可以自定义方法。自定义的方法如果是以大写字母开头,并且仅由字母和数字组成,会自动支持命令行补全; 59 | #alias {EXAMPLE.Test} { 60 | warnLog EXAMPLE.Test 执行一次。; 61 | #showme {测试触发1}; 62 | #showme {测试触发2}; 63 | }; 64 | 65 | #nop 无论是纯模块,还是弱模块,都有几个预定义的变量可供插件使用。; 66 | #nop 下面演示 $ID 变量的用法。; 67 | #action {^测试触发1$ID$} { 68 | #echo {可以看到,本触发具有一个由模块名标识构成的唯一 ID,因此不会和别的模块冲突。}; 69 | #action {%*测试触发%*}; 70 | infoLog 触发1成功。; 71 | }; 72 | 73 | #nop 一般建议 $ID 放在末尾。如果 $ID 和末尾锚定符号 $ 连用,那么也可以简写成 $E,效果相同; 74 | #action {^测试触发2$E} { 75 | infoLog 触发2成功。; 76 | }; 77 | -------------------------------------------------------------------------------- /plugins/basic/login.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 模块名称:几个常用的别名 5 | 模块说明:本文件属于框架代码的一部分,不建议修改。如有需求请在 GitHub 发 issue 或者 PR 6 | 版权声明:本文件属于 PaoTin++ 的一部分 7 | =========== 8 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 9 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 10 | =========== 11 | */ 12 | 13 | #var login[session] {}; 14 | #var login[user] {}; 15 | #var login[autoexec] {}; 16 | 17 | #alias {login} { 18 | #var login[session] {%1}; 19 | #var login[user] {%2}; 20 | #var login[autoexec] {%3}; 21 | #session {$session[name]} {$session[host]} {$session[port]}; 22 | }; 23 | 24 | #event {SESSION CONNECTED} { 25 | #if { "%0" !== "$login[session][name]" } { 26 | #return; 27 | }; 28 | 29 | login.auto-login; 30 | }; 31 | 32 | #alias {login.login-success} { 33 | #local type {%1}; 34 | 35 | #class login.auto-login kill; 36 | #undelay login.login-success; 37 | 38 | #showme 登录成功。; 39 | 40 | #nop 如果角色设置了 MXP 支持,则在建连之后主动向服务器发起 MXP 协商。; 41 | #nop 否则需要主动回车以放弃 MXP 协商。; 42 | #if { "$user[MXP]" == "true" } { 43 | load-lib mxp; 44 | #if { @existsAlias{mxp.Enable} } { 45 | mxp.Enable; 46 | }; 47 | #else { 48 | #cr; 49 | }; 50 | }; 51 | #else { 52 | #cr; 53 | }; 54 | 55 | #if { @isFalse{$login[user][manual]} } { 56 | login.online $type; 57 | #return; 58 | }; 59 | 60 | #class login.finger open; 61 | 62 | #action {^%u目前正在连线中。$} { 63 | #var login[user][name] {%%1}; 64 | #delay login.finger.done {login.finger.done} 0; 65 | }; 66 | 67 | #alias {login.finger.done} { 68 | #class login.finger kill; 69 | login.write-id-file; 70 | login.init-game; 71 | login.online; 72 | }; 73 | 74 | #delay login.finger.fail {login.finger.done} 5; 75 | 76 | #class login.finger close; 77 | 78 | finger $login[user][id]; 79 | }; 80 | 81 | #alias {login.online} { 82 | #local type {%1}; 83 | #local handler $login[autoexec]; 84 | kill-module login; 85 | 86 | $handler; 87 | 88 | #if { "$user[GMCP]" == "true" } { 89 | #delay 1 { 90 | load-module gmcp; 91 | gmcp.${gCurrentMUDLIB}.Enable; 92 | }; 93 | }; 94 | }; 95 | 96 | #alias {login.init-game} { 97 | #if { "$login[user][id]" == "" || "$user[id]" != "" } { 98 | #return; 99 | }; 100 | 101 | #var user[id] {$login[user][id]}; 102 | #var user[passwd] {$login[user][passwd]}; 103 | #gts #var user[id] {$login[user][id]}; 104 | #gts #var user[passwd] {$login[user][passwd]}; 105 | 106 | #local path {$user[id]}; 107 | #if { "$session[log_path]" != "" } { 108 | #local path {$session[log_path]}; 109 | }; 110 | 111 | #if { !@InitLog{$path} } { 112 | #echo {<119>创建日志目录 $gLog[PATH]/$path 时遇到错误。<299>}; 113 | #echo {<139>请检查你的安装环境,或者参考使用手册重新安装本软件。<299>}; 114 | #return; 115 | }; 116 | 117 | log.Open; 118 | #screen set title GAME-$user[id]; 119 | }; 120 | 121 | #alias {login.write-id-file} { 122 | #if { "$login[user][id]" == "" || "$user[id]" != "" } { 123 | #return; 124 | }; 125 | 126 | #local id-file {ids/$login[user][id]}; 127 | 128 | #if { @existsDir{var/ids} } { 129 | #local id-file {var/$id-file}; 130 | }; 131 | 132 | #if { !@existsFile{$id-file} } { 133 | #scan {file} {ids/$gCurrentMUDLIB} {login.write-id-file.do {$id-file} {&1}}; 134 | }; 135 | 136 | #if { "@uname{}" == "Windows_NT" } { 137 | windows-terminal-setup {$login[user][id]} {$id-file}; 138 | #local menu {$login[user][id]@$gCurrentMUDLIB}; 139 | NOTE 菜单已更新,请重启 Windows Terminal 并选择 $menu 进入游戏。交流请加 QQ 群: 951665549 暗号:「PaoTin++ for Windows」。; 140 | }; 141 | #else { 142 | NOTE 启动配置文件已生成,下次你可以直接 start $login[user][id] 进入游戏。; 143 | }; 144 | }; 145 | 146 | #nop 因为 #scan 命令和 #replace 命令都要用 &1,会存在冲突,因此需要分开两个 #alias 来写。; 147 | #alias {login.write-id-file.do} { 148 | #local file {%1}; 149 | #local content {%2}; 150 | 151 | #replace {content} {#var user[id] %s \x7b%*\x7d;} {#var user[id] &1 {$login[user][id]};}; 152 | #replace {content} {#var user[name] %s \x7b%*\x7d;} {#var user[name] &1 {$login[user][name]};}; 153 | #replace {content} {#var user[passwd] %s \x7b%*\x7d;} {#var user[passwd] &1 {$login[user][passwd]};}; 154 | #replace {content} {#nop 上面三处修改完毕之后,请删除下面这行文字:;\n} {}; 155 | #replace {content} {#var user[manual] %s \x7b%*\x7d;\n} {}; 156 | #line quiet #log remove $file; 157 | #line log $file {$content\}; 158 | }; 159 | 160 | #alias {windows-terminal-setup} { 161 | #local id {%1}; 162 | #local id-file {%2}; 163 | 164 | load-config windows; 165 | 166 | #local json {\x7b\r 167 | \x20\x20"profiles":\r 168 | \x20\x20{\r 169 | \x20\x20\x20\x20"defaults": {},\r 170 | \x20\x20\x20\x20"list": \r 171 | \x20\x20\x20\x20[\r 172 | \x20\x20\x20\x20\x20\x20{\r 173 | \x20\x20\x20\x20\x20\x20\x20\x20"commandline": "$gPaoTinPath/bin/tt++.exe $id-file",\r 174 | \x20\x20\x20\x20\x20\x20\x20\x20"hidden": false,\r 175 | \x20\x20\x20\x20\x20\x20\x20\x20"name": "$id@$gCurrentMUDLIB",\r 176 | \x20\x20\x20\x20\x20\x20\x20\x20"startingDirectory": "$gPaoTinPath"\r 177 | \x20\x20\x20\x20\x20\x20}\r 178 | \x20\x20\x20\x20]\r 179 | \x20\x20}\r 180 | \x7d\r 181 | }; 182 | 183 | #local appdata {@getenv{LOCALAPPDATA}}; 184 | #local file {$appdata/Microsoft/Windows Terminal/Fragments/PaoTin++/${id}.json}; 185 | #line quiet #line remove {$file}; 186 | #line log {$file} {$json}; 187 | }; 188 | -------------------------------------------------------------------------------- /plugins/lib/alert.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | ///=== { 10 | ///// 全局警报器是一个公共模块,负责统一管理客户端各模块发出的警报信息, 11 | ///// 并提供统一的操作界面。 12 | ///// }; 13 | 14 | #var lib_alert[META] { 15 | {NAME} {全局警报器} 16 | {DESC} {统一管理全局警报信息} 17 | {AUTHOR} {担子炮} 18 | }; 19 | 20 | #nop 如果警报已经被看到,则短时间内不会重复报警。; 21 | VAR {上次看到警报的时间,时间戳} alert-focus {0}; 22 | VAR {目前正在进行的警报,列表表格} alert-items {}; 23 | 24 | load-lib storage; 25 | 26 | #func {lib_alert.Init} { 27 | storage.Load {alert} {alert-items}; 28 | #return {true}; 29 | }; 30 | 31 | ///=== { 32 | // ## alert.Add <警报原因> [<间隔时间>] [<持续时间>] 33 | // 发出一个警报。每隔一段时间,就用蜂鸣器发出提醒声音。 34 | // 警报必须要有个原因,该原因连同解除该警报的按钮一起,会通过 prompt 显示在信息栏上。 35 | // 玩家可以用不同长短的间隔时间来表达不同的急促程度或其它含义,默认 60 秒。 36 | // 玩家也可以提供一个可选的持续时间,一旦超过持续时间,则警报将自动解除。默认持续时间为 3600 秒。 37 | // 间隔时间和持续时间均以秒为单位。 38 | // 进入警报状态后,某些终端可能会弹出桌面提醒。 39 | // 如果开启了蜂鸣器或者声音播放,你可能会听到声音。 40 | // 警报声会在玩家敲击任意键后暂时抑制,但除非手动关闭了所有信息栏的提示,否则一分钟后警报仍会继续。 41 | // }; 42 | #alias {alert.Add} { 43 | #local reason {%1}; 44 | #local interval {@defaultNum{%2;60}}; 45 | #local duration {@defaultNum{%3;3600}}; 46 | 47 | #if { @str.Width{reason} == 0 } { 48 | xtt.Usage alert.Add; 49 | #return; 50 | }; 51 | 52 | alert.Remove {$reason}; 53 | 54 | #list alert-items add {{ 55 | {id} {@str.Plain{$reason}} 56 | {reason} {$reason} 57 | {begin} {@time.Now{}} 58 | {duration} {$duration} 59 | {interval} {$interval} 60 | }}; 61 | 62 | storage.Save {alert} {alert-items}; 63 | 64 | alert.perform; 65 | }; 66 | 67 | ///=== { 68 | // ## alert.Remove <警报原因> 69 | // 解除警报。 70 | // }; 71 | #alias {alert.Remove} { 72 | #local id {@str.Plain{%1}}; 73 | #local idx {}; 74 | 75 | #if { &alert-items[] == 0 } { 76 | #return; 77 | }; 78 | 79 | #list alert-items indexate {id}; 80 | #list alert-items find {$id} idx; 81 | 82 | #if { $idx > 0 } { 83 | #list alert-items delete {$idx}; 84 | storage.Save {alert} {alert-items}; 85 | alert.perform; 86 | }; 87 | }; 88 | 89 | #nop 警报持续期间,用本命令来检查并及时删除过期的警报。; 90 | #alias {alert.check} { 91 | #local duration {}; 92 | 93 | #local changed {false}; 94 | 95 | #while { &alert-items[] > 0 } { 96 | #local begin {$alert-items[1][begin]}; 97 | #local duration {$alert-items[1][duration]}; 98 | 99 | #local duration {@math.Eval{ $begin + $duration - @time.Now{} }}; 100 | #if { $duration <= 0 } { 101 | #list alert-items delete 1; 102 | #local changed {true}; 103 | }; 104 | #else { 105 | #break; 106 | }; 107 | }; 108 | 109 | #if { @isTrue{$changed} } { 110 | storage.Save {alert} {alert-items}; 111 | alert.perform; 112 | }; 113 | 114 | #if { &alert-items[] > 0 } { 115 | #delay alert.check {alert.check} $duration; 116 | }; 117 | }; 118 | 119 | #alias {alert.beep} { 120 | #if { @time.Now{} - $alert-focus > 60 } { 121 | #bell; 122 | }; 123 | }; 124 | 125 | #alias {alert.perform} { 126 | #untick alert.beep; 127 | 128 | #if { &alert-items[] == 0 } { 129 | #var alert-focus {0}; 130 | #undelay alert.check; 131 | #unevent {RECEIVED KEYPRESS}; 132 | prompt.Set {{alert}{}}; 133 | #return; 134 | }; 135 | 136 | #list alert-items indexate {interval}; 137 | #list alert-items order; 138 | 139 | #local interval {$alert-items[1][interval]}; 140 | xtt.Tick alert.beep {alert.beep} $interval; 141 | 142 | #event {RECEIVED KEYPRESS} {#var alert-focus {@time.Now{}}}; 143 | 144 | #list alert-items indexate {begin}; 145 | #list alert-items order; 146 | 147 | alert.check; 148 | 149 | #local alerts {@fp.Map{{$alert-items}; VALUE[reason]<299>【<129>\@mslp.Exec{alert.Remove VALUE[id];了然}<299>】}}; 150 | #list alerts {collapse} { }; 151 | prompt.Set {{alert}{$alerts}}; 152 | }; 153 | 154 | #alias {alert.test} { 155 | #nop 奇怪的线索会有奖励; 156 | alert.Add {<129>奇怪的线索}; 157 | #nop 推车密信需要及时处理; 158 | alert.Add {<139>推车密信} 30; 159 | #nop 推车乱入提醒手动处理; 160 | alert.Add <119>推车乱入 10; 161 | #nop 比武大会十五分钟内有效; 162 | alert.Add {<139>比武大会} {120} {900}; 163 | }; 164 | -------------------------------------------------------------------------------- /plugins/lib/ga.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分 5 | =========== 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | =========== 9 | */ 10 | 11 | #var lib_ga[META] { 12 | {NAME} {GA} 13 | {DESC} {GA 处理。IAC GA 是服务器信息输出结尾符号} 14 | {AUTHOR} {担子炮} 15 | {NOTE} {本文件属于 PaoTin++ 的一部分} 16 | }; 17 | 18 | #config {IAC GA} {ON}; 19 | 20 | #func {lib_ga.Init} { 21 | #class data/lib/ga open; 22 | #list gXttPipeLine create {}; 23 | #class data/lib/ga close; 24 | 25 | #return true; 26 | }; 27 | 28 | load-lib telnet; 29 | load-lib event; 30 | 31 | event.Define {GA} {有参} {$MODULE} {接收到 IAC GA 时发射本事件,参数为造成本次输出的命令}; 32 | 33 | #event {SENT OUTPUT} { 34 | #local cmd {%0}; 35 | #replace cmd {{$TELNET[CR]?}$TELNET[LF]} {}; 36 | #if { "$cmd" != "" } { 37 | #list gXttPipeLine add {$cmd}; 38 | }; 39 | }; 40 | 41 | #event {RECEIVED PROMPT} { 42 | #local rawLine {%0}; 43 | #local line {%1}; 44 | #if { "$line" == "== 未完继续 %*== (%*继续下一页%*)%*" } { 45 | #line gag; 46 | #cr; 47 | #return; 48 | }; 49 | }; 50 | 51 | #action {^> {$TELNET[GA]|$TELNET[EOR]}$} { 52 | #local line {%0}; 53 | #replace line {{$TELNET[GA]|$TELNET[EOR]}$} {}; 54 | 55 | #local cmd {}; 56 | 57 | #if { &gXttPipeLine[] > 0 } { 58 | #local cmd {$gXttPipeLine[1]}; 59 | #list gXttPipeLine delete {1}; 60 | }; 61 | 62 | event.Emit GA {} {$cmd}; 63 | 64 | #line gag; 65 | 66 | } {1.000}; 67 | 68 | #alias {ga.Off} { 69 | #send {$TELNET[IAC]$TELNET[WILL]$TELNET[LINEMODE]\}; 70 | #send {$TELNET[IAC]$TELNET[DO]$TELNET[SGA]\}; 71 | }; 72 | 73 | #alias {ga.On} { 74 | #send {$TELNET[IAC]$TELNET[WILL]$TELNET[LINEMODE]\}; 75 | #send {$TELNET[IAC]$TELNET[DONT]$TELNET[SGA]\}; 76 | }; 77 | 78 | #alias {ga.Sync} { 79 | sync.Wait {#0}; 80 | }; 81 | 82 | #alias {ga.Confirm} { 83 | #local cmd {%1}; 84 | 85 | #while { &gXttPipeLine[] > 0 && {$gXttPipeLine[1]} !== {$cmd} } { 86 | #list gXttPipeLine delete {1}; 87 | }; 88 | }; 89 | 90 | #func {ga.ThisCmd} { 91 | #return {$gXttPipeLine[1]}; 92 | }; 93 | 94 | #func {ga.IsUnderway} { 95 | #if { &gXttPipeLine[] > 1 } { 96 | #return 1; 97 | }; 98 | #else { 99 | #return 0; 100 | }; 101 | }; 102 | -------------------------------------------------------------------------------- /plugins/lib/gmcp.tin: -------------------------------------------------------------------------------- 1 | #nop GMCP 支持库; 2 | #nop vim: set filetype=tt:; 3 | 4 | /* 5 | 本文件属于 PaoTin++ 的一部分。 6 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 7 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 8 | */ 9 | 10 | load-lib telnet; 11 | load-lib option; 12 | 13 | option.Define {GMCPDebug} {Bool} {是否显示 GMCP 原始数据} {false}; 14 | 15 | #alias {gmcp.Enable} { 16 | #event {IAC WILL GMCP} { 17 | #send {$TELNET[IAC]$TELNET[DO]$TELNET[GMCP]\}; 18 | }; 19 | }; 20 | 21 | #var gGMCP {}; 22 | 23 | #alias {gmcp.ToggleDebug} {option.Toggle GMCPDebug}; 24 | -------------------------------------------------------------------------------- /plugins/lib/storage.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | ///=== { 10 | ///// storage 模块实现了一个通用的本地存储引擎。 11 | ///// 可以用来存储和载入变量,这允许其它模块可以持久化自己的数据。 12 | ///// 变量会被存储在本地文件系统当中,称之为「存储文件」。 13 | ///// 14 | ///// 存储文件按规定会统一存放在 data 目录下,支持文件重定位。也就是说, 15 | ///// 对于存储文件 file1 来说,其可能的物理存放位置为: 16 | ///// 1. var/data/file1.tin (优先) 17 | ///// 2. data/file1.tin (其次) 18 | ///// 19 | ///// 各模块可以通过 API storage.Save 按需创建自己的存储文件,在其中存储一个或多个 20 | ///// 变量,并在需要时通过 API storage.Load 提取这些变量。 21 | ///// 22 | ///// 另外,storage 模块自行维护了一个全局存储文件。在简单使用场景下,其它模块 23 | ///// 可以通过 API storage.SetGlobal 和 storage.GetGlobal 来读写这个全局存储文件。 24 | ///// 这对读写少量的数据显得更加方便。 25 | ///// }; 26 | 27 | #var lib_storage[META] { 28 | {NAME} {通用存储引擎} 29 | {DESC} {可以存储和载入变量,这允许其它模块可以持久化自己的数据} 30 | {AUTHOR} {担子炮} 31 | }; 32 | 33 | VAR {存储路径} storage-path {data}; 34 | 35 | #func {lib_storage.Init} { 36 | #local _ {@mkdir{data}}; 37 | 38 | #local files {}; 39 | #line quiet #scan dir {var/data/} files; 40 | #if { &files[] > 0 } { 41 | #var storage-path {var/data}; 42 | }; 43 | 44 | storage.Load {storage} {storage-globals}; 45 | 46 | dbgLog storage 全局存储项已加载。; 47 | 48 | #return {true}; 49 | }; 50 | 51 | ///=== { 52 | // ## storage.Save <文件名> <变量名1> [...] 53 | // 将由变量名列表所指定的变量及其值存储到指定的存储文件中。 54 | // }; 55 | #alias {storage.Save} { 56 | #local file {%1}; 57 | #local vars {%2}; 58 | 59 | #if { "$file" == "" || "$vars" == "" } { 60 | xtt.Usage storage.Save; 61 | #return; 62 | }; 63 | 64 | #class comm-store-tmp open; 65 | #local var {}; 66 | #foreach {$vars} {var} { 67 | #local count {&{${var}[]}}; 68 | #if { $count < 100 } { 69 | #var {dump-$var} {${$var}}; 70 | dbgLog storage 变量 $var 已写入磁盘。共有 @math.Max{$count;1} 个数据项。; 71 | #continue; 72 | }; 73 | 74 | #local idx {0}; 75 | #loop 1 {$count} {idx} { 76 | #local key {*{${var}[+$idx]}}; 77 | #local value {${${var}[+$idx]}}; 78 | #var {dump-${var}[$key]} {$value}; 79 | }; 80 | 81 | dbgLog storage 变量 $var 已写入磁盘。共有 $count 个数据项。; 82 | }; 83 | #class comm-store-tmp close; 84 | 85 | #class comm-store-tmp write {$storage-path/${file}.tin}; 86 | 87 | #class comm-store-tmp kill; 88 | }; 89 | 90 | ///=== { 91 | // ## storage.Load <文件名> <变量名1> [...] 92 | // 从指定的存储文件中加载变量。 93 | // 存储文件中实际存储的变量可能更多一些,但本函数可以只加载其中一部分变量。 94 | // }; 95 | #alias {storage.Load} { 96 | #local file {%1}; 97 | #local vars {%2}; 98 | 99 | #if { "$file" == "" || "$vars" == "" } { 100 | xtt.Usage storage.Load; 101 | #return; 102 | }; 103 | 104 | #line quiet #class comm-store-tmp {assign} {load-file data/${file}.tin}; 105 | #local var {}; 106 | #foreach {$vars} {var} { 107 | #local count {&{dump-${var}[]}}; 108 | #if { $count < 100 } { 109 | #var {$var} {${dump-$var}}; 110 | dbgLog storage 已从磁盘中加载变量 $var,共有 @math.Max{$count;1} 个数据项。; 111 | #continue; 112 | }; 113 | 114 | #local idx {0}; 115 | #loop 1 {$count} {idx} { 116 | #local key {*{dump-${var}[+$idx]}}; 117 | #local value {${dump-${var}[+$idx]}}; 118 | #var {${var}[$key]} {$value}; 119 | }; 120 | 121 | dbgLog storage 已从磁盘中加载变量 $var,共有 $count 个数据项。; 122 | }; 123 | #class comm-store-tmp kill; 124 | }; 125 | 126 | ///=== { 127 | // ## storage.SetGlobal [<值>] 128 | // 将值关联到 KEY 上,并存储到全局存储文件中。 129 | // 参见 storage.GetGlobal 130 | // }; 131 | #alias {storage.SetGlobal} { 132 | #local key {%1}; 133 | #local value {%2}; 134 | 135 | #if { "$key" == "" } { 136 | xtt.Usage storage.SetGlobal; 137 | #return; 138 | }; 139 | 140 | #var storage-globals[$key] {$value}; 141 | storage.Save {storage} {storage-globals}; 142 | 143 | dbgLog storage 全局存储项已写入磁盘。; 144 | }; 145 | 146 | ///=== { 147 | // #@ storage.GetGlobal 148 | // 从全局存储文件中根据 KEY 提取值。 149 | // 参见 storage.SetGlobal 150 | // }; 151 | #func {storage.GetGlobal} { 152 | #local key {%1}; 153 | 154 | #if { "$key" == "" } { 155 | xtt.Usage storage.SetGlobal; 156 | #return {}; 157 | }; 158 | 159 | #return {$storage-globals[$key]}; 160 | }; 161 | -------------------------------------------------------------------------------- /plugins/lib/tab-completion.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #event {READ FILE} { 10 | refresh-tab-completion; 11 | }; 12 | 13 | #alias {refresh-tab-completion} { 14 | #info {ALIASES} save; 15 | #local idx {}; 16 | #foreach {*info[ALIASES][]} {idx} { 17 | #local name {$info[ALIASES][$idx][arg1]}; 18 | #nop 只有名字规整的 alias 才给加自动补全。; 19 | #if { "$name" == "%*{[^a-zA-Z0-9.-]}%*" } { 20 | #continue; 21 | }; 22 | 23 | #nop 名字是大骆驼风格命名的,可以加自动补全。; 24 | #if { "$name" == "{[A-Z][a-zA-Z0-9]*}" } { 25 | #tab {$name}; 26 | #continue; 27 | }; 28 | 29 | #nop 如果名字由小数点分成多个部分,那么只有最后一部分是大写字母开头的,才加自动补全。; 30 | #if { "$name" == "%*.{[A-Z][A-Za-z0-9]*}" } { 31 | #tab {$name}; 32 | #continue; 33 | }; 34 | }; 35 | }; 36 | 37 | #tab #action; 38 | #tab #alias; 39 | #tab #all; 40 | #tab #banner; 41 | #tab #bell; 42 | #tab #break; 43 | #tab #buffer; 44 | #tab #button; 45 | #tab #case; 46 | #tab #cat; 47 | #tab #chat; 48 | #tab #class; 49 | #tab #commands; 50 | #tab #config; 51 | #tab #continue; 52 | #tab #cr; 53 | #tab #cursor; 54 | #tab #daemon; 55 | #tab #debug; 56 | #tab #default; 57 | #tab #delay; 58 | #tab #dictionary; 59 | #tab #draw; 60 | #tab #echo; 61 | #tab #edit; 62 | #tab #else; 63 | #tab #elseif; 64 | #tab #end; 65 | #tab #event; 66 | #tab #foreach; 67 | #tab #format; 68 | #tab #function; 69 | #tab #gag; 70 | #tab #grep; 71 | #tab #help; 72 | #tab #highlight; 73 | #tab #history; 74 | #tab #if; 75 | #tab #ignore; 76 | #tab #info; 77 | #tab #kill; 78 | #tab #killall; 79 | #tab #line; 80 | #tab #list; 81 | #tab #local; 82 | #tab #log; 83 | #tab #loop; 84 | #tab #macro; 85 | #tab #map; 86 | #tab #match; 87 | #tab #math; 88 | #tab #message; 89 | #tab #nop; 90 | #tab #parse; 91 | #tab #path; 92 | #tab #pathdir; 93 | #tab #port; 94 | #tab #prompt; 95 | #tab #read; 96 | #tab #regexp; 97 | #tab #replace; 98 | #tab #return; 99 | #tab #run; 100 | #tab #scan; 101 | #tab #screen; 102 | #tab #script; 103 | #tab #send; 104 | #tab #session; 105 | #tab #showme; 106 | #tab #snoop; 107 | #tab #split; 108 | #tab #ssl; 109 | #tab #substitute; 110 | #tab #switch; 111 | #tab #system; 112 | #tab #tab; 113 | #tab #test; 114 | #tab #textin; 115 | #tab #ticker; 116 | #tab #unaction; 117 | #tab #unalias; 118 | #tab #unbutton; 119 | #tab #undelay; 120 | #tab #unevent; 121 | #tab #unfunction; 122 | #tab #ungag; 123 | #tab #unhighlight; 124 | #tab #unlocal; 125 | #tab #unmacro; 126 | #tab #unpathdir; 127 | #tab #unprompt; 128 | #tab #unsplit; 129 | #tab #unsubstitute; 130 | #tab #untab; 131 | #tab #unticker; 132 | #tab #unvariable; 133 | #tab #variable; 134 | #tab #while; 135 | #tab #write; 136 | #tab #zap; 137 | -------------------------------------------------------------------------------- /plugins/lib/telnet.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #var TELNET[IAC] {\xFF}; 10 | #var TELNET[DONT] {\xFE}; 11 | #var TELNET[DO] {\xFD}; 12 | #var TELNET[WONT] {\xFC}; 13 | #var TELNET[WILL] {\xFB}; 14 | #var TELNET[SB] {\xFA}; 15 | #var TELNET[SE] {\xF0}; 16 | #var TELNET[GMCP] {\xC9}; 17 | #var TELNET[MXP] {\x5B}; 18 | #var TELNET[CR] {\x0D}; 19 | #var TELNET[LF] {\x0A}; 20 | #var TELNET[GA] {\xF9}; 21 | #var TELNET[EOR] {\xEF}; 22 | #var TELNET[SGA] {\x03}; 23 | #var TELNET[LINEMODE] {\x22}; 24 | -------------------------------------------------------------------------------- /plugins/lib/ui/__main__.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | load-module lib/ui/tmux; 4 | load-module lib/ui/prompt; 5 | load-module lib/ui/beautify; 6 | load-module lib/ui/keyboard; 7 | 8 | /* 9 | 鼠标支持尚不完善,和 tmux 相比还很弱, 10 | 但 mslp 又不得不用,所以开不开让玩家自己选择吧。不喜欢 mslp 的用户建议关掉它。 11 | */ 12 | #if { "$user[MOUSE]" == "true" } { 13 | load-module lib/ui/mouse; 14 | }; 15 | 16 | load-module lib/ui/chat; 17 | load-module lib/ui/move; 18 | -------------------------------------------------------------------------------- /plugins/lib/ui/beautify.tin: -------------------------------------------------------------------------------- 1 | #var lib_ui_beautify[META] { 2 | {NAME} {美化插件} 3 | {DESC} {美化你的 TinTin++,对齐你的表格。} 4 | {AUTHOR} {担子炮} 5 | }; 6 | 7 | #func {lib_ui_beautify.Init} { 8 | #class data/lib/ui/beautify open; 9 | #var beautify-switch {OFF}; 10 | #var beautify-eol-mark {.}; 11 | #class data/lib/ui/beautify close; 12 | beautify.On; 13 | #return true; 14 | }; 15 | 16 | #var beautify-table { 17 | {┌|┎|╭|╓|└|┖|╰|╙} {─} 18 | {─|┬|┭|┰|┱|╥} {─} 19 | {├|┞|┟|┠|╟} {─} 20 | {┼|╁|╀|╂|┽|╃|╅|╉|╫} {─} 21 | {┴|┵|┸|┹|╨} {─} 22 | {┏|┍|┗|┕} {━} 23 | {━|┳|┲|┯|┮} {━} 24 | {┣|┢|┡|┝} {━} 25 | {╋|╇|╈|┿|╊|╆|╄|┾} {━} 26 | {┻|┺|┷|┶} {━} 27 | {╔|╦|╠|╬|╚|╩|═|╒|╤|╞|╪|╘|╧} {═} 28 | 29 | {│|┃|║|┆|┇|┊|┋|┤|┨|┫|╣|╢|╡|▕} {left-align} 30 | {┐|┓|┒|╮|╗|┘|┛|┚|╯|╝|╲|╱|∕} {left-align} 31 | 32 | {■|‖|▉|▊|▋|▌|▍|▎|▏|◢|◣|◥|◤|▓} {left-align} 33 | {▁|▂|▃|▄|▅|▆|▇|█|▀|▔|┄|┅|┈|┉|—|ˉ} {double} 34 | 35 | {▼|▲|△|▽|□|◇|◆|☆|★} {left-align} 36 | {•|◎|⊙|☉|○|●|Θ|⊕|Ο} {left-align} 37 | {·|∶|∴|∷|˙|¨} {left-align} 38 | {≤|≧|≥|≮|≯|∨|∧|≌|╳|×} {left-align} 39 | {↙|↘|↗|↖|←|↑|→|↓} {left-align} 40 | {※|Ψ|Ж|ξ|ф|∩|⊥|♀|∞|≈|√|⌒|Ω|¤|ō||ψ|ζ|∽} {left-align} 41 | {①|②|③|④|⑤|⑥|⑦|⑧|⑨|⑩} {left-align} 42 | 43 | {“|‘} {right-align} 44 | {”|’|…} {left-align} 45 | 46 | {⭕️|🐎|🌊|🔥|🎭|🐀|🔅} {left-align} 47 | }; 48 | 49 | #alias {beautify.On} { 50 | #class beautify-sub kill; 51 | #class beautify-sub open; 52 | 53 | #local charset {}; 54 | #foreach {*{beautify-table[]}} {charset} { 55 | #local type {${beautify-table[$charset]}}; 56 | #switch {"$type"} { 57 | #case {"double"} {#substitute {{$charset}} {%%1%%1}}; 58 | #case {"left-align"} {#substitute {{$charset}} {%%1 }}; 59 | #case {"right-align"} {#substitute {{$charset}} { %%1}}; 60 | #default {#line sub var #substitute {{$charset}} {%%1$type}}; 61 | }; 62 | }; 63 | 64 | #substitute {%S%!s{https?://[[:graph:]]+}\s*{\S|$}} {%%1 %%2 %%3}; 65 | 66 | #nop 行尾空格因为肉眼不可见因此常常导致无法匹配触发。这里把它可视化一下。; 67 | #action {~^%*%+1S%+1..s%c{|ID=beautify}$} { 68 | #line ignore #showme {@Beautify{{%%1%%2%%3}}$beautify-eol-mark}; 69 | #line gag; 70 | } {9.999}; 71 | 72 | #class beautify-sub close; 73 | 74 | #var beautify-switch {ON}; 75 | okLog 宽字符美化已启用。; 76 | warnLog 出于美化需要,接下来你在屏幕上看到的内容可能和服务器实际传送的内容不一致。; 77 | warnLog 这可能会给编写触发带来困扰,此时你可以通过快捷键或者 beautify.Off 暂时禁用美化。; 78 | 79 | prompt.Set {{beautify}{<129>已启用}}; 80 | prompt.refresh; 81 | }; 82 | 83 | #alias {beautify.Off} { 84 | #class beautify-sub kill; 85 | 86 | #var beautify-switch {OFF}; 87 | warnLog 宽字符美化已禁用。你可以通过快捷键或者 beautify.On 重新启用。; 88 | 89 | prompt.Set {{beautify}{<119>已禁用}}; 90 | prompt.refresh; 91 | }; 92 | 93 | #func {Beautify} { 94 | #local text {%1}; 95 | 96 | #if { "${beautify-switch}" != "ON" } { 97 | #return {$text}; 98 | }; 99 | 100 | #local charset {}; 101 | #foreach {*{beautify-table[]}} {charset} { 102 | #local type {${beautify-table[$charset]}}; 103 | #switch {"$type"} { 104 | #case {"double"} {#replace {text} {{$charset}} {&1&1}}; 105 | #case {"left-align"} {#replace {text} {{$charset}} {&1 }}; 106 | #case {"right-align"} {#replace {text} {{$charset}} { &1}}; 107 | #default {#line sub var #replace {text} {{$charset}} {&1$type}}; 108 | }; 109 | }; 110 | 111 | #return {$text}; 112 | }; 113 | 114 | #alias {beautify.SetEolMark} { 115 | #local mark {%1}; 116 | 117 | #if { "$mark" == "" } { 118 | #local mark {.}; 119 | }; 120 | 121 | #var beautify-eol-mark {$mark}; 122 | }; 123 | 124 | #alias {beautify.ToggleSwitch} { 125 | #if { "${beautify-switch}" == "ON" } { 126 | beautify.Off; 127 | }; 128 | #else { 129 | beautify.On; 130 | }; 131 | }; 132 | -------------------------------------------------------------------------------- /plugins/lib/ui/mouse.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | #var lib_ui_mouse[META] { 4 | {NAME} {鼠标支持} 5 | {DESC} {支持鼠标和触摸板操作,允许上下翻屏、点击按钮} 6 | {AUTHOR} {担子炮} 7 | }; 8 | 9 | #config {mouse} on; 10 | 11 | load-lib ui/keyboard; 12 | 13 | #event {SCROLLED CTRL MOUSE WHEEL DOWN} { 14 | #if {%2 * -1 <= $prompt-bot-max-line + 1 } { 15 | #cursor {history next}; 16 | }; 17 | #else { 18 | #local info {}; 19 | #buffer info save info; 20 | #if { $info[LINE] < 0 || $info[LINE] >= $info[USED] } { 21 | keyboard.NormalMode; 22 | #return; 23 | }; 24 | keyboard.LessMode; 25 | #buffer down 1; 26 | }; 27 | }; 28 | 29 | #event {SCROLLED CTRL MOUSE WHEEL UP} { 30 | #if {%2 * -1 <= $prompt-bot-max-line + 1 } { 31 | #cursor {history prev}; 32 | }; 33 | #else { 34 | #local info {}; 35 | #buffer info save info; 36 | #if { $info[LINE] == 0 } { 37 | #return; 38 | }; 39 | keyboard.LessMode; 40 | #buffer up 1; 41 | }; 42 | }; 43 | 44 | #event {SCROLLED MOUSE WHEEL DOWN} { 45 | #if {%2 * -1 <= $prompt-bot-max-line + 1 } { 46 | #cursor {history next}; 47 | }; 48 | #else { 49 | #local info {}; 50 | #buffer info save info; 51 | #if { $info[LINE] < 0 || $info[LINE] >= $info[USED] } { 52 | keyboard.NormalMode; 53 | #return; 54 | }; 55 | keyboard.LessMode; 56 | #buffer down 10; 57 | }; 58 | }; 59 | 60 | #event {SCROLLED MOUSE WHEEL UP} { 61 | #if {%2 * -1 <= $prompt-bot-max-line + 1 } { 62 | #cursor {history prev}; 63 | }; 64 | #else { 65 | #local info {}; 66 | #buffer info save info; 67 | #if { $info[LINE] == 0 } { 68 | #return; 69 | }; 70 | keyboard.LessMode; 71 | #buffer up 10; 72 | }; 73 | }; 74 | 75 | #event {SHORT-CLICKED LINK SEND MOUSE BUTTON ONE} { 76 | xtt.Send {%4}; 77 | }; 78 | 79 | #event {SHORT-CLICKED LINK EXEC MOUSE BUTTON ONE} { 80 | %4; 81 | }; 82 | -------------------------------------------------------------------------------- /plugins/lib/xtintin/__init__.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #nop xtintin 是为了方便 TinTin++ 使用而增加的一些语法扩展。; 10 | 11 | load-file plugins/lib/xtintin/doc.tin; 12 | load-file plugins/lib/xtintin/number.tin; 13 | load-file plugins/lib/xtintin/bool.tin; 14 | load-file plugins/lib/xtintin/string.tin; 15 | load-file plugins/lib/xtintin/algo.tin; 16 | load-file plugins/lib/xtintin/list.tin; 17 | load-file plugins/lib/xtintin/table.tin; 18 | load-file plugins/lib/xtintin/slist.tin; 19 | load-file plugins/lib/xtintin/set.tin; 20 | load-file plugins/lib/xtintin/queue.tin; 21 | load-file plugins/lib/xtintin/time.tin; 22 | load-file plugins/lib/xtintin/path.tin; 23 | load-file plugins/lib/xtintin/debug.tin; 24 | load-file plugins/lib/xtintin/fp.tin; 25 | load-file plugins/lib/xtintin/default.tin; 26 | load-file plugins/lib/xtintin/cmds.tin; 27 | load-file plugins/lib/xtintin/mslp.tin; 28 | load-file plugins/lib/xtintin/buffer.tin; 29 | load-file plugins/lib/xtintin/id.tin; 30 | 31 | ///=== { 32 | ///// 以上文档采用 PaoTin++ 的文档化注释工具生成。 33 | ///// 下面是关于文档化注释工具的简单介绍。 34 | ///// 35 | // ## xtt.Doc 36 | // 解析文档化注释,生成文档。 37 | // 38 | // 文档化注释是 PaoTin++ 非常有特色的创举,是对 tt++ 能力的一大增强,可以实现 39 | // 以下八合一效果: 40 | // - 代码注释: 文档化注释既是文档,也是注释。它和被说明的源代码紧密结合 41 | // 在一起,既方便查看,也方便维护,可以有效避免文档过时的问题。 42 | // - 联机帮助: 文档化注释可以在 PaoTin++ 运行时,即使是在游戏界面,也可以 43 | // 随时查看,支持模糊查询。也和 PaoTin++ 其它部分做紧密结合。 44 | // - 错误提示: 文档化注释可以给别名当成是参数输入错误时提示用户正确用法的 45 | // 作用。具体参见 <139>HELP xtt.Usage<299>。 46 | // - Markdown: 文档化注释用类似于 Markdown 的标记语言书写,可以轻松输出为 47 | // Markdown 文档。 48 | // - HTML文档: 借助于 TinTin++ 的 HTML 输出功能,文档化注释也可以输出成彩 49 | // 色 HTML 页面。 50 | // - GH-Pages: 生成的 HTML 页面可以部署在 GitHub Pages 上,作为项目的文档 51 | // 页面使用。 52 | // - 示例代码: 文档化注释里可以写示例代码,帮助用户更好地理解文档。示例 53 | // 代码有别于普通的文本,它会被识别并高亮显示给用户。 54 | // - 单元测试: 符合规范的示例代码本身也可以作为单元测试来使用,方便在长期 55 | // 维护过程中,保持代码的健壮性和可靠性。 56 | // 57 | // 先给大家看一个直观的例子。 58 | // 用户只需要按照下面这个格式,就可以为模块和函数(或别名)书写文档。 59 | // <119>注意:<299>在下面的模版中,「格式非常重要」,一定要按照格式书写,才会 60 | // 被正确识别。 61 | // 62 | // ///=== { 63 | // \/\/\/\/\/ 模块名称,和模块简要说明 64 | // \/\/\/\/\/ 65 | // \/\/\/\/\/ 模块的整体说明性的文档,建议放在文件的一开头。 66 | // \/\/\/\/\/ 67 | // \/\/\/\/\/ 五个斜线(<169>\/\/\/\/\/<299>)开头的文本将会被识别为模块文档。 68 | // \/\/\/\/\/ 模块文档仅在 HELP <模块名> 时出现,如果只查看函数或者别名, 69 | // \/\/\/\/\/ 是不会显示的。 70 | // \/\/\/\/\/ 71 | // \/\/\/\/\/ 这里继续写模块文档。 72 | // \/\/\/\/\/ 73 | // \/\/\/\/\/ 下面开始写别名和函数的文档: 74 | // \/\/\/\/\/ 两个斜线(<169>\/\/<299>)开头的文本会被识别为别名和函数的文档。 75 | // \/\/\/\/\/ 76 | // \/\/\/\/\/ 建议放在被说明的别名或者函数的紧挨着的前面。 77 | // \/\/\/\/\/ 78 | // \/\/\/\/\/ 实际运用中,文档和代码一般都是穿插书写,最终所有文档会按照书写 79 | // \/\/\/\/\/ 时的顺序提取成一篇完整的文档,因此书写顺序非常重要。请自行体会。 80 | // \/\/ 81 | // \/\/ <169>\#\#<299> foo.Bar <参数1> <参数2> 82 | // \/\/ 函数(或别名)的一句话介绍。 83 | // \/\/ 函数(或别名)的更多介绍。 84 | // \/\/ 函数(或别名)的参数说明: 85 | // \/\/ - 参数1: 参数说明 86 | // \/\/ - 参数2: 参数说明 87 | // \/\/ }; 88 | // #alias {foo.Bar} { 89 | // ....; 90 | // }; 91 | // 92 | // 上面的例子中,如果是为别名写文档,则应该用 <169>\#\#<299> 开头,如果为函数写文档, 93 | // 则应当以 <169>\#\@<299> 开头。 94 | // 95 | // 参数格式说明: 96 | // - 必选参数: 用 <参数名> 表示 97 | // - 可选参数: 用 [<参数名>] 表示 98 | // - 重复参数: 用 ... 或 [...] 表示 99 | // 100 | // 参数名一般用具有实际意义的中文名称来表示,或者用参数类型表示。 101 | // 有关参数类型的更多内容请查看 <139>HELP DataType<299>。 102 | // 103 | // ## xtt.Usage <关键字> [<错误提示>] 104 | // 给出关键字对应的帮助信息,以提示用户了解如何正确使用该别名(或函数)。 105 | // }; 106 | 107 | load-file framework/doc.tin; 108 | -------------------------------------------------------------------------------- /plugins/lib/xtintin/algo.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #nop 本文件是 xtintin 的一部分,实现了一些常用算法; 10 | 11 | ///=== { 12 | // #@ algo.Diff <字符串1> <字符串2> 13 | // 如果两个字符串参数中,其中一个是另一个插入了一个子串的结果,则本函数可计算并返回该子串。 14 | // 15 | // EXAMPLE: @algo.Diff{一二三四五六七八;一二三四PaoTin++五六七八} 16 | // RESULT: PaoTin++ 17 | // }; 18 | #func {algo.Diff} { 19 | #local str1 {%1}; 20 | #local str2 {%2}; 21 | 22 | #list str1 {tokenize} {$str1}; 23 | #list str2 {tokenize} {$str2}; 24 | 25 | #local size1 {&str1[]}; 26 | #local size2 {&str2[]}; 27 | 28 | #if { $size1 < $size2 } { 29 | #local tmp {$str1}; 30 | #local str1 {$str2}; 31 | #local str2 {$tmp}; 32 | #local tmp {$size1}; 33 | #local size1 {$size2}; 34 | #local size2 {$tmp}; 35 | }; 36 | 37 | #local idx {}; 38 | #loop {1} {$size2} {idx} { 39 | #local ch1 {$str1[$idx]}; 40 | #local ch2 {$str2[$idx]}; 41 | #if { "$ch1" != "$ch2" } { 42 | #break; 43 | }; 44 | }; 45 | 46 | #local begin {$idx}; 47 | #local end {}; 48 | #math end {$size1 - $size2 + $begin - 1}; 49 | #local diff {}; 50 | #loop {$begin} {$end} {idx} { 51 | #local ch {$str1[$idx]}; 52 | #cat diff {$ch}; 53 | }; 54 | 55 | #return {$diff}; 56 | }; 57 | 58 | ///=== { 59 | // #@ algo.ParseVertLayout <竖排文字串> 60 | // 将竖排文字重新排版成横排,方便做匹配。 61 | // 结果是一个列表,其中的每个元素代表一行转换后的文本。 62 | // 为方便计算对齐,其中竖排文字串要求用竖线定出每行的左右边界,且一个汉字必须对应两个空格。 63 | // 64 | // EXAMPLE: 65 | // #nop 需要将每一行竖排文字都拼接到一起,左右两边用竖线定界(重要); 66 | // #local vert {|谁 纵 千 救 眼 三 将 闲 事 十 银 赵 侠| 67 | // |能 死 秋 赵 花 杯 炙 过 了 步 鞍 客 | 68 | // |书 侠 二 挥 耳 吐 啖 信 拂 杀 照 缦 客| 69 | // |阁 骨 壮 金 热 然 朱 陵 衣 一 白 胡 | 70 | // |下 香 士 槌 后 诺 亥 饮 去 人 马 缨 行| 71 | // |, , , , , , , , , , , , | 72 | // |白 不 洹 邯 意 五 持 脱 深 千 飒 吴 | 73 | // |首 惭 赫 郸 气 岳 觞 剑 藏 里 沓 钩 | 74 | // |太 世 大 先 素 倒 劝 膝 身 不 如 霜 | 75 | // |玄 上 梁 震 霓 为 侯 前 与 留 流 雪 | 76 | // |经 英 城 惊 生 轻 嬴 横 名 行 星 明 | 77 | // |。 。 。 。 。 。 。 。 。 。 。 。 |}; 78 | // 79 | // #local lines {\@algo.ParseVertLayout{\$vert}}; 80 | // #local lineNo {}; 81 | // #foreach {*lines[]} {lineNo} { 82 | // #echo {%s} {\$lines[\$lineNo]}; 83 | // }; 84 | // 85 | // OUTPUT: 86 | // 侠 客 行 87 | // 赵客缦胡缨,吴钩霜雪明。 88 | // 银鞍照白马,飒沓如流星。 89 | // 十步杀一人,千里不留行。 90 | // 事了拂衣去,深藏身与名。 91 | // 闲过信陵饮,脱剑膝前横。 92 | // 将炙啖朱亥,持觞劝侯嬴。 93 | // 三杯吐然诺,五岳倒为轻。 94 | // 眼花耳热后,意气素霓生。 95 | // 救赵挥金槌,邯郸先震惊。 96 | // 千秋二壮士,洹赫大梁城。 97 | // 纵死侠骨香,不惭世上英。 98 | // 谁能书阁下,白首太玄经。 99 | // }; 100 | #func {algo.ParseVertLayout} { 101 | #local text {%0}; 102 | #replace text {| |} {|}; 103 | #replace text {^|} {}; 104 | #replace text {|$} {}; 105 | 106 | #local ch {}; 107 | #local lines {}; 108 | #local state {left}; 109 | #local colNo {1}; 110 | #local newline {|}; 111 | 112 | #parse {$text} {ch} { 113 | #switch {"$state/$ch"} { 114 | #case {"left/ "} { 115 | #local state {right}; 116 | }; 117 | #case {"right/ "} { 118 | #local state {left}; 119 | #local lines[$colNo] {$lines[$colNo] }; 120 | #math colNo {$colNo + 1}; 121 | }; 122 | #case {"%*/$newline"} { 123 | #math colNo {1}; 124 | #local state {left}; 125 | }; 126 | #case {"left/%*"} { 127 | #local lines[$colNo] {$lines[$colNo]$ch}; 128 | #math colNo {$colNo + 1}; 129 | }; 130 | #default { 131 | errLog find BUG; 132 | #return {}; 133 | }; 134 | }; 135 | }; 136 | 137 | #local output {}; 138 | #loop {&lines[]} {1} {colNo} { 139 | #replace {lines[$colNo]} {%+1..s$} {}; 140 | #if { {$lines[$colNo]} == {%*%+1..S%*} } { 141 | #list output add {{$lines[$colNo]}}; 142 | }; 143 | }; 144 | 145 | #return {$output}; 146 | }; 147 | -------------------------------------------------------------------------------- /plugins/lib/xtintin/bool.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #nop 本文件是 xtintin 的一部分,实现了一些布尔运算函数; 10 | 11 | ///=== { 12 | ///// 布尔运算函数: 13 | // 14 | // #@ true 15 | // 永远为真。 16 | // 17 | // #@ false 18 | // 永远为假。 19 | // }; 20 | #func {true} {#return 1}; 21 | #func {false} {#return 0}; 22 | 23 | ///=== { 24 | // #@ isTrue <值> 25 | // 判断一个值是否为字符串 true 或者整数值 1,如果是则返回真,否则反之。 26 | // 27 | // #@ isFalse <值> 28 | // 判断一个值是否为字符串 false 或者整数值 0,如果是则返回真,否则反之。 29 | // }; 30 | #func {isTrue} { 31 | #if { "%0" == "{true|1}" } { 32 | #return 1; 33 | }; 34 | #else { 35 | #return 0; 36 | }; 37 | }; 38 | 39 | #func {isFalse} { 40 | #if { "%0" == "{false|0}" } { 41 | #return 1; 42 | }; 43 | #else { 44 | #return 0; 45 | }; 46 | }; 47 | 48 | ///=== { 49 | // #@ allTrue <值1> [<值2> ...] 50 | // 判断多个值,是否全部为真,如果是则返回真,否则反之。 51 | // }; 52 | #func {allTrue} { 53 | #local value {}; 54 | #foreach {%0} {value} { 55 | #if @isFalse{$value} { 56 | #return 0; 57 | }; 58 | }; 59 | 60 | #return 1; 61 | }; 62 | 63 | ///=== { 64 | // #@ allFalse <值1> [<值2> ...] 65 | // 判断多个值,是否全部为假,如果是则返回真,否则反之。 66 | // }; 67 | #func {allFalse} { 68 | #local value {}; 69 | #foreach {%0} {value} { 70 | #if @isTrue{$value} { 71 | #return 0; 72 | }; 73 | }; 74 | 75 | #return 1; 76 | }; 77 | 78 | ///=== { 79 | // #@ anyTrue <值1> [<值2> ...] 80 | // 判断多个值,是否有任意一个为真,如果是则返回真,否则反之。 81 | // }; 82 | #func {anyTrue} { 83 | #if @allFalse{%0} { 84 | #return 0; 85 | }; 86 | #else { 87 | #return 1; 88 | }; 89 | }; 90 | 91 | ///=== { 92 | // #@ anyFalse <值1> [<值2> ...] 93 | // 判断多个值,是否有任意一个为假,如果是则返回真,否则反之。 94 | // }; 95 | #func {anyFalse} { 96 | #if @allTrue{%0} { 97 | #return 0; 98 | }; 99 | #else { 100 | #return 1; 101 | }; 102 | }; 103 | 104 | ///=== { 105 | // #@ if <条件> <值1> [<值2>] 106 | // 三元运算符。判断条件,如果条件成立则给出值1,否则给出值2,如果值2未提供则视同为空字符串。 107 | // }; 108 | #func {if} { 109 | #local cond {%1}; 110 | #local then {%2}; 111 | #local else {%3}; 112 | 113 | #if { $cond } { 114 | #return {$then}; 115 | }; 116 | #else { 117 | #return {$else}; 118 | }; 119 | }; 120 | -------------------------------------------------------------------------------- /plugins/lib/xtintin/buffer.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #nop 本文件是 xtintin 的一部分,实现了一些 buffer 工具函数。; 10 | 11 | ///=== { 12 | ///// buffer 操作函数 13 | ///// 14 | // ## buffer.RawLine 15 | // 打印当前触发行的原始文本,而不论当前触发器是否为颜色触发。 16 | // }; 17 | #alias {buffer.RawLine} { 18 | #line quiet #info output save; 19 | #echo {%s} {$info[OUTPUT][LINE]}; 20 | }; 21 | 22 | ///=== { 23 | // ## buffer.PlainLine 24 | // 打印当前触发行的无色文本,而不论当前触发器是否为颜色触发。 25 | // }; 26 | #alias {buffer.PlainLine} { 27 | #line quiet #info output save; 28 | #echo {%s} {@str.Plain{$info[OUTPUT][LINE]}}; 29 | }; 30 | 31 | ///=== { 32 | // #@ buffer.RawLine 33 | // 获得当前触发行的原始文本,而不论当前触发器是否为颜色触发。 34 | // }; 35 | #func {buffer.RawLine} { 36 | #line quiet #info output save; 37 | #return {$info[OUTPUT][LINE]}; 38 | }; 39 | 40 | ///=== { 41 | // #@ buffer.PlainLine 42 | // 获得当前触发行的无色文本,而不论当前触发器是否为颜色触发。 43 | // }; 44 | #func {buffer.PlainLine} { 45 | #line quiet #info output save; 46 | #local line {$info[OUTPUT][LINE]}; 47 | #return {@str.Plain{$line}}; 48 | }; 49 | 50 | ///=== { 51 | // #@ buffer.GetRawLine <行号> [<截止行号>] 52 | // 获得缓冲区中的文本,以原始格式返回。行号从 -1 开始往上数,最后一行为 -1。 53 | // 如果只提供一个行号,则返回字符串,否则返回列表。 54 | // }; 55 | #func {buffer.GetRawLine} { 56 | #local lineNo {%1}; 57 | #local endNo {%2}; 58 | 59 | #if { "$endNo" != "" } { 60 | #if { $endNo < $lineNo } { 61 | #local tmp $endNo; 62 | #local endNo $lineNo; 63 | #local lineNo $tmp; 64 | }; 65 | }; 66 | 67 | #local line {}; 68 | #line quiet #buffer {get} {line} {$lineNo} {$endNo}; 69 | 70 | #return {$line}; 71 | }; 72 | 73 | ///=== { 74 | // #@ buffer.GetPlainLine <行号> [<截止行号>] 75 | // 获得缓冲区中的文本,以无色格式返回。行号从 -1 开始往上数,最后一行为 -1。 76 | // 如果只提供一个行号,则返回字符串,否则返回列表。 77 | // }; 78 | #func {buffer.GetPlainLine} { 79 | #local lineNo {%1}; 80 | #local endNo {%2}; 81 | 82 | #if { "$endNo" != "" } { 83 | #if { $endNo < $lineNo } { 84 | #local tmp $endNo; 85 | #local endNo $lineNo; 86 | #local lineNo $tmp; 87 | }; 88 | }; 89 | 90 | #local line {}; 91 | #line quiet #buffer {get} {line} {$lineNo} {$endNo}; 92 | 93 | #if { "$endNo" == "" } { 94 | #return {@str.Plain{$line}}; 95 | }; 96 | #else { 97 | #foreach {*line[]} {lineNo} { 98 | #local line[$lineNo] {@str.Plain{$line[$lineNo]}}; 99 | }; 100 | #return {$line}; 101 | }; 102 | }; 103 | -------------------------------------------------------------------------------- /plugins/lib/xtintin/debug.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #nop ############################ 调试开关 #################################; 10 | 11 | #var XTinTin[debug] {false}; 12 | 13 | ///=== { 14 | ///// 调试开关函数: 15 | ///// 16 | ///// 由于 beautify 和 prompt 插件都会带来大量的代码调用, 17 | ///// 所以为了尽量减少调试信息,设计了这组函数以开启或者关闭调试开关, 18 | ///// 并在调试开启期间,临时禁用 beautify 和 prompt 插件。 19 | // 20 | // ## xtt.ToggleDebug 21 | // 切换调试状态。 22 | // }; 23 | #alias {xtt.ToggleDebug} { 24 | #if { "$XTinTin[debug]" == "false" } { 25 | xtt.DebugOn; 26 | }; 27 | #else { 28 | xtt.DebugOff; 29 | }; 30 | }; 31 | 32 | ///=== { 33 | // ## xtt.DebugOn 34 | // 开启调试状态。 35 | // }; 36 | #alias {xtt.DebugOn} { 37 | #var XTinTin[debug] {true}; 38 | beautify.Off; 39 | prompt.Disable; 40 | #line quiet #debug all on; 41 | }; 42 | 43 | ///=== { 44 | // ## xtt.DebugOff 45 | // 关闭调试状态。 46 | // }; 47 | #alias {xtt.DebugOff} { 48 | #var XTinTin[debug] {false}; 49 | #line quiet #debug all off; 50 | prompt.Enable; 51 | beautify.On; 52 | }; 53 | -------------------------------------------------------------------------------- /plugins/lib/xtintin/default.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #nop 本文件是 xtintin 的一部分,实现了一些默认值处理函数。; 10 | 11 | ///=== { 12 | ///// 默认值处理函数: 13 | // 14 | // #@ isEmpty <字符串> 15 | // 判断字符串是否为空,为空则返回真。 16 | // }; 17 | #func {isEmpty} { 18 | #local value {%0}; 19 | 20 | #if { "$value" == "" } { 21 | #return 1; 22 | }; 23 | 24 | #return 0; 25 | }; 26 | 27 | ///=== { 28 | // #@ default <字符串> <默认值> 29 | // 判断字符串是否为空,为空则返回默认值。 30 | // }; 31 | #func {default} { 32 | #local value {%1}; 33 | #local default {%2}; 34 | 35 | #if { @isEmpty{$value} } { 36 | #return {$default}; 37 | }; 38 | 39 | #return {$value}; 40 | }; 41 | 42 | ///=== { 43 | // #@ defaultNum <字符串> <默认值> 44 | // 如果字符串为空,或者格式不像是一个数值(允许负数、小数)、或者等于 0,则返回默认值。 45 | // }; 46 | #func {defaultNum} { 47 | #local value {%1}; 48 | #local default {%2}; 49 | 50 | #if { @isEmpty{$value} } { 51 | #return {$default}; 52 | }; 53 | 54 | #if { "$value" != "{-|}%+1..d{|\.\d+}" } { 55 | #return {$default}; 56 | }; 57 | 58 | #if { $value == 0 } { 59 | #return {$default}; 60 | }; 61 | 62 | #return {$value}; 63 | }; 64 | 65 | ///=== { 66 | // #@ isEmptyVar <字符串> 67 | // 判断参数是否为空,或者变量展开失败。如果是则返回真。 68 | // }; 69 | #func {isEmptyVar} { 70 | #local value {%0}; 71 | 72 | #if { "$value" == "" } { 73 | #return 1; 74 | }; 75 | 76 | #if { "$value" == "$%*" } { 77 | #return 1; 78 | }; 79 | 80 | #return 0; 81 | }; 82 | 83 | ///=== { 84 | // #@ defaultVar <字符串> <默认值> 85 | // 判断参数是否为空,或者变量展开失败。如果是则返回默认值。 86 | // }; 87 | #func {defaultVar} { 88 | #local value {%1}; 89 | #local default {%2}; 90 | 91 | #if { @isEmptyVar{$value} } { 92 | #return {$default}; 93 | }; 94 | 95 | #return {$value}; 96 | }; 97 | 98 | ///=== { 99 | // #@ defaultNumVar <字符串> <默认值> 100 | // 判断参数是否不像是一个数字,或者变量展开失败。如果是则返回默认值。 101 | // 102 | ///// 由于 TinTin++ 的变量展开有个特点,那就是如果不存在变量 foo,则 \$foo 展开的 103 | ///// 结果并不是空字符串,而是字符串 "\$foo",会导致误以为不是空串。 104 | ///// 以上三个函数将该情形也视同为空字符串并做相应处理。 105 | // }; 106 | #func {defaultNumVar} { 107 | #local value {%1}; 108 | #local default {%2}; 109 | 110 | #if { @isEmptyVar{$value} } { 111 | #return {$default}; 112 | }; 113 | 114 | #if { "$value" != "{-|}%+1..d{|\.\d+}" } { 115 | #return {$default}; 116 | }; 117 | 118 | #if { $value == 0 } { 119 | #return {$default}; 120 | }; 121 | 122 | #return {$value}; 123 | }; 124 | -------------------------------------------------------------------------------- /plugins/lib/xtintin/id.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #nop 本文件是 xtintin 的一部分,实现了自增 ID 功能。; 10 | 11 | VAR {自增 ID 当前值} {id.ID} {}; 12 | 13 | ///=== { 14 | ///// 自增 ID 相关函数 15 | ///// 16 | // #@ ID [] 17 | // 取出新 ID,其值比上次自动加一。 18 | // 可选的 KEY 是自增计数器的标识符,如果担心和其它代码冲突,可以提供一个别致的标识符。 19 | // }; 20 | #func {ID} { 21 | #local key {@default{%1;__DEFAULT__}}; 22 | #math id.ID[$key] {$id.ID[$key] + 1}; 23 | #return {$id.ID[$key]}; 24 | }; 25 | 26 | ///=== { 27 | // ## id.Reset [] 28 | // 重置 ID,下次将取到 1。 29 | // 可选的 KEY 是自增计数器的标识符,如果担心和其它代码冲突,可以提供一个别致的标识符。 30 | // }; 31 | #alias {id.Reset} { 32 | #local key {@default{%1;__DEFAULT__}}; 33 | #unvar id.ID[$key]; 34 | }; 35 | -------------------------------------------------------------------------------- /plugins/lib/xtintin/mslp.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #nop 本文件是 xtintin 的一部分,实现了一些 MSLP 工具函数。; 10 | 11 | ///=== { 12 | ///// MSLP 工具函数: 13 | // 14 | // #@ mslp.Send <命令> <文本> 15 | // 生成链接。当点击指定的文本时,会向服务器发送指定的命令。本函数返回生成的链接代码,#echo 后即可生效。 16 | // }; 17 | #func {mslp.Send} { 18 | #local send {%1}; 19 | #local text {%2}; 20 | #return {\e]68;1;SEND;$send\a\e[4m$text\e[24m}; 21 | }; 22 | 23 | ///=== { 24 | // #@ mslp.Exec <代码> <文本> 25 | // 生成链接。当点击指定的文本时,会执行指定的代码。本函数返回生成的链接代码,#echo 后即可生效。 26 | // }; 27 | #func {mslp.Exec} { 28 | #local exec {%1}; 29 | #local text {%2}; 30 | #return {\e]68;1;EXEC;$exec\a\e[4m$text\e[24m}; 31 | }; 32 | 33 | ///=== { 34 | // #@ mslp.Help <关键字> <文本> 35 | // 生成链接。当点击指定的文本时,会执行 HELP <关键字>。本函数返回生成的链接代码,#echo 后即可生效。 36 | // }; 37 | #func {mslp.Help} { 38 | #local word {%1}; 39 | #local text {%2}; 40 | #return {\e]68;1;EXEC;HELP $word\a\e[4m$text\e[24m}; 41 | }; 42 | 43 | ///=== { 44 | // #@ mslp.Module <模块名称> <文本> 45 | // 生成链接。当点击指定的文本时,会执行 MOD <模块名称>。本函数返回生成的链接代码,#echo 后即可生效。 46 | // }; 47 | #func {mslp.Module} { 48 | #local name {%1}; 49 | #local text {%2}; 50 | #return {\e]68;1;EXEC;MOD $name\a\e[4m$text\e[24m}; 51 | }; 52 | 53 | #alias {xtt.mslp-helper} { 54 | #local cmd {%1}; 55 | $cmd; 56 | #buffer end; 57 | }; 58 | 59 | ///=== { 60 | // #@ mslp.TinTin <关键字> <文本> 61 | // 生成链接。当点击指定的文本时,会执行 #help <关键字>。本函数返回生成的链接代码,#echo 后即可生效。 62 | // }; 63 | #func {mslp.TinTin} { 64 | #local word {%1}; 65 | #local text {%2}; 66 | #return {\e]68;1;EXEC;xtt.mslp-helper {#help $word}\a\e[4m$text\e[24m}; 67 | }; 68 | 69 | ///=== { 70 | // #@ mslp.Var <变量名> <文本> 71 | // 生成链接。当点击指定的文本时,会显示变量的值。本函数返回生成的链接代码,#echo 后即可生效。 72 | // }; 73 | #func {mslp.Var} { 74 | #local name {%1}; 75 | #local text {%2}; 76 | #return {\e]68;1;EXEC;xtt.mslp-helper {#var $name}\a\e[4m$text\e[24m}; 77 | }; 78 | 79 | ///=== { 80 | // #@ mslp.Alias <别名> <文本> 81 | // 生成链接。当点击指定的文本时,会显示别名的代码。本函数返回生成的链接代码,#echo 后即可生效。 82 | // }; 83 | #func {mslp.Alias} { 84 | #local name {%1}; 85 | #local text {%2}; 86 | #return {\e]68;1;EXEC;xtt.mslp-helper {#alias $name}\a\e[4m$text\e[24m}; 87 | }; 88 | 89 | ///=== { 90 | ///// MSLP 需要鼠标支持。请确认你的终端已经正确配置了鼠标,并在 PaoTin++ 中打开了鼠标支持。 91 | // }; 92 | -------------------------------------------------------------------------------- /plugins/lib/xtintin/table.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #nop 本文件是 xtintin 的一部分,实现了一些表格处理函数; 10 | 11 | ///=== { 12 | ///// 表格处理函数: 13 | // 14 | // #@ table.Keys <表格变量名> [<键名通配符>] 15 | // 提取表格中所有符合通配符的键名,结果以 slist 格式返回。 16 | // }; 17 | #func {table.Keys} { 18 | #local table.local.table {%1}; 19 | #local table.local.pattern {@default{%2;%*}}; 20 | 21 | #local len {&{${table.local.table}[]}}; 22 | #if { $len == 0 } { 23 | #return {}; 24 | }; 25 | 26 | #local keys {}; 27 | 28 | #loop 1 {&{${table.local.table}[]}} {idx} { 29 | #local key {*{${table.local.table}[+$idx]}}; 30 | #if { "$key" == "$table.local.pattern" } { 31 | #cat keys {*{${table.local.table}[+$idx]};}; 32 | }; 33 | }; 34 | 35 | #replace {keys} {;$} {}; 36 | 37 | #return {$keys}; 38 | }; 39 | 40 | ///=== { 41 | // #@ table.Values <表格变量名> [<值的通配符>] 42 | // 提取表格中所有符合通配符的值,结果以 slist 格式返回。 43 | // }; 44 | #func {table.Values} { 45 | #local table.local.table {%1}; 46 | #local table.local.pattern {@default{%2;%*}}; 47 | 48 | #local len {&{${table.local.table}[]}}; 49 | #if { $len == 0 } { 50 | #return {}; 51 | }; 52 | 53 | #local values {}; 54 | 55 | #loop 1 {&{${table.local.table}[]}} {idx} { 56 | #local value {${${table.local.table}[+$idx]}}; 57 | #if { "$value" == "$table.local.pattern" } { 58 | #cat values {${${table.local.table}[+$idx]};}; 59 | }; 60 | }; 61 | 62 | #replace {values} {;$} {}; 63 | 64 | #return {$values}; 65 | }; 66 | -------------------------------------------------------------------------------- /plugins/lib/xtintin/time.tin: -------------------------------------------------------------------------------- 1 | #nop vim: set filetype=tt:; 2 | 3 | /* 4 | 本文件属于 PaoTin++ 的一部分。 5 | PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp ) 享有并保留一切法律权利 6 | 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。 7 | */ 8 | 9 | #nop 本文件是 xtintin 的一部分,实现了一些时间处理函数; 10 | 11 | ///=== { 12 | ///// 时间处理函数: 13 | // 14 | // #@ time.Now 15 | // 返回当前系统时间戳。 16 | // }; 17 | #func {time.Now} {#format result {%T}}; 18 | 19 | ///=== { 20 | // #@ time.ParseDoC <时间长度文本> 21 | // 将中文书写的时间长度(Duration of Chinese)文本转换成以秒为单位的整数值。 22 | // }; 23 | #func {time.ParseDoC} { 24 | #local {timeStr} {%0}; 25 | 26 | #nop 兼容不同的写法; 27 | #replace {timeStr} {个} {}; 28 | #replace {timeStr} {星期} {周}; 29 | #replace {timeStr} {小时} {时}; 30 | #replace {timeStr} {分钟} {分}; 31 | 32 | #nop 注意这里用了个小技巧,末尾的空格不要去掉; 33 | #replace timeStr {%S年} {@math.ParseCN{&1}*31104000+ }; 34 | #replace timeStr {%S月} {@math.ParseCN{&1}*2592000+ }; 35 | #replace timeStr {%S周} {@math.ParseCN{&1}*604800+ }; 36 | #replace timeStr {%S天} {@math.ParseCN{&1}*86400+ }; 37 | #replace timeStr {%S时} {@math.ParseCN{&1}*3600+ }; 38 | #replace timeStr {%S分} {@math.ParseCN{&1}*60+ }; 39 | #replace timeStr {%S秒} {@math.ParseCN{&1}}; 40 | 41 | #local time {}; 42 | #math time {$timeStr + 0}; 43 | 44 | #return {$time}; 45 | }; 46 | 47 | ///=== { 48 | // #@ time.Format [<时间戳> [<格式字符串>]] 49 | // 将指定时间戳按照格式字符串要求,转换成字符串。 50 | // 格式字符串参见 #help time,如果省略则为 %Y-%m-%d %H:%M:%S。 51 | // 时间戳如果省略则为当前系统时间。 52 | // }; 53 | #func {time.Format} { 54 | #local time {@defaultNum{%1;@time.Now{}}}; 55 | #local format {@default{%2;{%Y-%m-%d %H:%M:%S}}}; 56 | #format result {%t} {{$format}{$time}}; 57 | #return {$result}; 58 | }; 59 | 60 | ///=== { 61 | // #@ time.FormatNow [<格式字符串>]] 62 | // 将指定时间戳按照格式字符串要求,转换成字符串。 63 | // 格式字符串参见 #help time,如果省略则为 %Y-%m-%d %H:%M:%S。 64 | // }; 65 | #func {time.FormatNow} { 66 | #local time {@time.Now{}}; 67 | #local format {@default{%1;{%Y-%m-%d %H:%M:%S}}}; 68 | #format result {%t} {{$format}{$time}}; 69 | #return {$result}; 70 | }; 71 | 72 | ///=== { 73 | // #@ time.Date [<时间戳>] 74 | // 将指定时间戳转换成 YYYY-mm-dd 格式的日期字符串。 75 | // 时间戳如果省略则为当前系统时间。 76 | // }; 77 | #func {time.Date} { 78 | #local time {@defaultNum{%1;@time.Now{}}}; 79 | #local format {@default{%2;{%Y-%m-%d}}}; 80 | #format result {%t} {{$format}{$time}}; 81 | #return {$result}; 82 | }; 83 | 84 | ///=== { 85 | // #@ time.Year [<时间戳>] 86 | // 将指定时间戳所在的年份,四位数字。 87 | // 时间戳如果省略则为当前系统时间。 88 | // }; 89 | #func {time.Year} { 90 | #local time {@defaultNum{%1;@time.Now{}}}; 91 | #local format {@default{%2;{%Y}}}; 92 | #format result {%t} {{$format}{$time}}; 93 | #return {$result}; 94 | }; 95 | 96 | ///=== { 97 | // #@ time.Month [<时间戳>] 98 | // 将指定时间戳所在的月份,两位数字。 99 | // 时间戳如果省略则为当前系统时间。 100 | // }; 101 | #func {time.Month} { 102 | #local time {@defaultNum{%1;@time.Now{}}}; 103 | #local format {@default{%2;{%m}}}; 104 | #format result {%t} {{$format}{$time}}; 105 | #return {$result}; 106 | }; 107 | 108 | ///=== { 109 | // #@ time.Day [<时间戳>] 110 | // 将指定时间戳所在的日期,两位数字。 111 | // 时间戳如果省略则为当前系统时间。 112 | // }; 113 | #func {time.Day} { 114 | #local time {@defaultNum{%1;@time.Now{}}}; 115 | #local format {@default{%2;{%d}}}; 116 | #format result {%t} {{$format}{$time}}; 117 | #return {$result}; 118 | }; 119 | 120 | ///=== { 121 | // #@ time.Time [<时间戳>] 122 | // 将指定时间戳转换成 HH:MM:SS 格式的时间字符串。 123 | // 时间戳如果省略则为当前系统时间。 124 | // }; 125 | #func {time.Time} { 126 | #local time {@defaultNum{%1;@time.Now{}}}; 127 | #local format {@default{%2;{%H:%M:%S}}}; 128 | #format result {%t} {{$format}{$time}}; 129 | #return {$result}; 130 | }; 131 | 132 | ///=== { 133 | // #@ time.Hour [<时间戳>] 134 | // 将指定时间戳所在时刻的小时值,24 小时制,两位数字。 135 | // 时间戳如果省略则为当前系统时间。 136 | // }; 137 | #func {time.Hour} { 138 | #local time {@defaultNum{%1;@time.Now{}}}; 139 | #local format {@default{%2;{%H}}}; 140 | #format result {%t} {{$format}{$time}}; 141 | #return {$result}; 142 | }; 143 | 144 | ///=== { 145 | // #@ time.Minute [<时间戳>] 146 | // 将指定时间戳所在时刻的分钟值,两位数字。 147 | // 时间戳如果省略则为当前系统时间。 148 | // }; 149 | #func {time.Minute} { 150 | #local time {@defaultNum{%1;@time.Now{}}}; 151 | #local format {@default{%2;{%M}}}; 152 | #format result {%t} {{$format}{$time}}; 153 | #return {$result}; 154 | }; 155 | 156 | ///=== { 157 | // #@ time.Second [<时间戳>] 158 | // 将指定时间戳所在时刻的秒值,两位数字。 159 | // 时间戳如果省略则为当前系统时间。 160 | // }; 161 | #func {time.Second} { 162 | #local time {@defaultNum{%1;@time.Now{}}}; 163 | #local format {@default{%2;{%S}}}; 164 | #format result {%t} {{$format}{$time}}; 165 | #return {$result}; 166 | }; 167 | -------------------------------------------------------------------------------- /plugins/silent.tin: -------------------------------------------------------------------------------- 1 | #nop 做一个安静的客户端。; 2 | #nop 「隐藏自己,做好清理。」—— 《三体·黑暗森林》; 3 | 4 | #nop 命令白名单。; 5 | #nop 只有纯小写字母,空格,减号组成的命令无需再加白名单。这里只维护例外; 6 | #var silent-WhiteList { 7 | chat; chat*; qq; qq*; 8 | helpme; helpme*; tt; tt*; 9 | rumor; rumor*; tell; say; 10 | bd; bd*; group; group*; 11 | nation; nation*; ask; i2; 12 | buy; list; node; fullme; 13 | shu; dang; verify; perform; 14 | register; check; answer; reply; 15 | 16 | amber_alert; status_me; set; unset; 17 | }; 18 | 19 | #nop 文字表情; 20 | #cat silent-WhiteList { 21 | ...; :)..; :D; :P; ?; 22 | ??; @@; ad.; 23 | }; 24 | 25 | #var silent-LastCmd {}; 26 | 27 | #alias {^%*{|ID=paotin/silent}$} { 28 | #local cmd {%0}; 29 | 30 | #if { {$cmd} === {} } { 31 | #return; 32 | }; 33 | 34 | #local ok {@silent.Check{$cmd}}; 35 | #if { $ok } { 36 | #var silent-LastCmd {}; 37 | xtt.Send {$cmd}; 38 | #return; 39 | }; 40 | 41 | #echo {<169>命令「<139>$cmd<169>」不是一个合法的 MUD 命令,如果确认是,请更新白名单。<099>}; 42 | 43 | #if { {$cmd} !== {$silent-LastCmd} } { 44 | #echo {<169>本次命令<119>已被抑制<169>。如果你确认命令没问题,可以先<139>回车重复<169>一次本命令,将临时通过一次。<099>}; 45 | #var silent-LastCmd {$cmd}; 46 | }; 47 | #else { 48 | #var silent-LastCmd {}; 49 | xtt.Send {%0}; 50 | }; 51 | } {9.999}; 52 | 53 | #func {silent.Check} { 54 | #local cmd {%0}; 55 | 56 | #replace {cmd} {^%S{| (.*)}$} { 57 | {cmd}{&1} 58 | {args}{&3} 59 | }; 60 | 61 | #local cmd {$cmd}; 62 | 63 | #nop 这个是 chat; 64 | #if { {$cmd[cmd]} == {'%*} } { 65 | #return 1; 66 | }; 67 | 68 | #nop 白名单通行; 69 | #if { {$silent-WhiteList[$cmd[cmd]]} == {true} } { 70 | #return 1; 71 | }; 72 | 73 | #nop 否则只接受指定格式的命令; 74 | #if { {$cmd[cmd]} != {{[a-z0-9_]{1,10}}} } { 75 | #return 0; 76 | }; 77 | 78 | #nop 参数不允许有特殊字符; 79 | #var silent-check-retcode {}; 80 | #regex {$cmd[args]} {{*UTF8}{^([a-z0-9. -]|\p{Han})*$}} { 81 | #var silent-check-retcode {1}; 82 | } { 83 | #var silent-check-retcode {0}; 84 | }; 85 | 86 | #local retcode {$silent-check-retcode}; 87 | #unvar silent-check-retcode; 88 | 89 | #return $retcode; 90 | }; 91 | 92 | #alias {silent.Init} { 93 | #local list {$silent-WhiteList}; 94 | #local cmd {}; 95 | #var silent-WhiteList {}; 96 | #foreach {$list} {cmd} { 97 | #var {silent-WhiteList[$cmd]} {true}; 98 | }; 99 | }; 100 | 101 | silent.Init; 102 | -------------------------------------------------------------------------------- /profile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export LANG=en_US.UTF8 4 | export TERM=xterm-256color 5 | 6 | export LSCOLORS=Gxfxcxdxbxegedabagacad 7 | export LESS="-r -f" 8 | export PATH=$HOME/bin:$PATH 9 | export PS1='\[\033[1;49;32m\]MUD\[\033[0m\]:\[\033[33m\]\w\[\033[0m\]\$ ' 10 | 11 | set -o vi 12 | 13 | bind '"\C-n": history-search-forward' 14 | bind '"\C-p": history-search-backward' 15 | 16 | alias ll='ls -l' 17 | alias l='ls -lah' 18 | alias vim=nvim 19 | alias vi=nvim 20 | -------------------------------------------------------------------------------- /setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | git submodule init 6 | (cd tintin && git fetch --all --prune) 7 | git submodule update --remote 8 | 9 | paths='/usr/local /opt/homebrew /usr/pkg' 10 | for path in $paths; do 11 | if [ -d $path/include ]; then 12 | export CFLAGS="${CFLAGS:+$CFLAGS }-I$path/include" 13 | export CPPFLAGS="${CPPFLAGS:+$CPPFLAGS }-I$path/include" 14 | export LDFLAGS="${LDFLAGS:+$LDFLAGS }-L$path/lib" 15 | fi 16 | done 17 | 18 | (cd tintin/src && ./configure && make) 19 | cp tintin/src/tt++ bin/ || exit 20 | 21 | if [ -L var -a -d "$(readlink var)" ]; then 22 | mkdir -p var/ids 23 | mkdir -p var/etc 24 | mkdir -p var/data 25 | mkdir -p var/log 26 | mkdir -p var/plugins 27 | else 28 | echo 请尽快建立 var 目录,以防止你的数据丢失。; 29 | echo 推荐将 var 目录建在别的地方,然后由 ./var 通过符号链接指向它。; 30 | fi 31 | 32 | mkdir -p .config/nvim 33 | ln -s ../../init.vim .config/nvim/init.vim 34 | 35 | mkdir -p .local/share/nvim/site/autoload/ 36 | ln -s ../../plugged/vim-plug/plug.vim .local/share/nvim/site/autoload/plug.vim 37 | 38 | mkdir -p .local/share/nvim/plugged 39 | cd .local/share/nvim/plugged 40 | 41 | git clone --depth 1 https://mirror.ghproxy.com/https://github.com/junegunn/vim-plug.git 42 | git clone --depth 1 https://mirror.ghproxy.com/https://github.com/dzpao/vim-mbs.git 43 | git clone --depth 1 https://mirror.ghproxy.com/https://github.com/morhetz/gruvbox.git 44 | git clone --depth 1 https://mirror.ghproxy.com/https://github.com/yegappan/mru.git 45 | git clone --depth 1 https://mirror.ghproxy.com/https://github.com/jlanzarotta/BufExplorer.git 46 | git clone --depth 1 https://mirror.ghproxy.com/https://github.com/mhinz/vim-startify.git 47 | -------------------------------------------------------------------------------- /tmux.conf: -------------------------------------------------------------------------------- 1 | # vim: set fdm=marker foldenable: 2 | 3 | ########## 基本设置 ---{{{ 4 | # 5 | # 把 prefix 设置成与 screen 相同,实际上也更好摁一些 6 | set -g prefix C-a 7 | unbind-key C-b 8 | bind-key a send-prefix 9 | 10 | set -g buffer-limit 20000; 11 | 12 | # 快捷键采用 vi 模式 13 | setw -g mode-keys vi 14 | 15 | # 设置默认 Shell 为 bash,但各平台路径各异,需要分别特殊处理 16 | if-shell 'test -x /data/data/com.termux/files/usr/bin/bash' { 17 | set -g default-shell /data/data/com.termux/files/usr/bin/bash 18 | bind-key -n Home send Escape "OH" 19 | bind-key -n End send Escape "OF" 20 | } { 21 | if-shell 'test -x /usr/local/bin/bash' { 22 | set -g default-shell /usr/local/bin/bash 23 | } { 24 | if-shell 'test -x /usr/bin/bash' { 25 | set -g default-shell /usr/bin/bash 26 | } { 27 | set -g default-shell /bin/bash 28 | } 29 | } 30 | } 31 | # }}} 32 | 33 | ########## 终端属性设置 ---{{{ 34 | # 全面开启鼠标支持,鼠标可以用来选择窗口及面板,调整面板尺寸,以及复制文本 35 | set -g mouse on 36 | 37 | # Use xterm function key sequence 38 | setw -g xterm-keys on 39 | 40 | # 默认终端 41 | set -g default-terminal "xterm-256color" 42 | # Terminal overrides 43 | set -g set-clipboard on 44 | set -g terminal-overrides "*88col*:colors=88,*256col*:colors=256,xterm*:colors=256,*256col*:Tc,*:Ms" 45 | 46 | # 这个不能关,关了进了 vim 再退出屏幕画面不会恢复到进入之前 47 | setw -g alternate-screen on 48 | # }}} 49 | 50 | ########## 状态栏设置 ---{{{ 51 | # 52 | # 开启状态栏,并在接下来定制状态栏样式 53 | set -g status on 54 | # 不关心其它窗口的事件,对强迫症来说太烦人了 55 | setw -g monitor-activity off 56 | # 每隔 10 秒刷新一次状态栏 57 | set -g status-interval 10 58 | # 状态栏的窗口列表靠左显示 59 | set -g status-justify left 60 | # 状态栏的整体色调背景色为蓝色 61 | set -g status-style bg=#0000AA 62 | # 状态栏左部最大长度不超过 32 63 | set -g status-left-length 32 64 | # 状态栏左部的格式: 亮品红的日期时间,然后是亮黄色的主机名(#h) 65 | set -g status-left "#[fg=magenta,bold]%m-%d %H:%M #[fg=brightyellow,bold]#h #[fg=red,bold]| " 66 | # 状态栏右部最大长度不超过 32 67 | set -g status-right-length 32 68 | # 状态栏右部显示内容 69 | set -g status-right "#[fg=brightyellow]| #(tmux-ui LOGO)#[default]" 70 | # 状态栏窗口列表中,每个窗口的显示格式: 绿色的编号(#I)及窗口名称(#W) 71 | setw -g window-status-format '#[fg=green]#I-#(tmux-ui WIN #{pane_title})#[default]' 72 | # 状态栏窗口列表中,当前窗口的显示格式: 黄色的编号(#I)及窗口名称(#W) 73 | setw -g window-status-current-format '#[fg=brightyellow]#I-#(tmux-ui WIN #{pane_title})#[default]' 74 | # 状态栏窗口列表中,有事件的窗口的显示风格 75 | setw -g window-status-activity-style fg=red,bold,bg=default 76 | # 状态栏窗口列表中,有喇叭的窗口的显示风格 77 | setw -g window-status-bell-style fg=red,bold,bg=default 78 | # Pane 的边框格式 79 | set -g pane-border-format '#(tmux-ui PANE #{pane_title} #{pane_active} #{pane_width})' 80 | # 固定给每个 pane 顶部显示一个边框 81 | set -g pane-border-status top 82 | # 设置边框颜色 83 | set -g pane-active-border-style "bg=colour236,fg=cyan bold" 84 | set -g pane-border-style "bg=colour234,fg=white" 85 | # tmux 的信息也会显示在状态栏,包括 tmux 的命令行 86 | set -g message-style fg=brightyellow,bold,bg=red 87 | # 设置信息显示时间 88 | set -g display-time 1000 89 | # }}} 90 | 91 | ########## 窗口管理 ---{{{ 92 | # 93 | # 窗口的索引值从 0 开始 94 | set -g base-index 0 95 | # 关闭中间的会自动重新编号 96 | # set -g renumber-windows on 97 | 98 | set -g detach-on-destroy off 99 | 100 | # 类似于 screen,用 Ctrl+C 创建窗口,故意映射两组,防止按错 101 | bind-key C-c new-window -c '#{pane_current_path}' 102 | bind-key c new-window -c '#{pane_current_path}' 103 | 104 | # 自动设置窗口标题,一旦主动设置过窗口标题,则此窗口不会再次自动设置名称 105 | setw -g automatic-rename on 106 | 107 | # 通过 Ctrl+n / Ctrl+p 来切换窗口 108 | bind-key C-n next-window 109 | bind-key n next-window 110 | bind-key C-p previous-window 111 | bind-key p previous-window 112 | 113 | # 快速在两个窗口之间切换 114 | bind-key C-a last-window 115 | 116 | # 显示所有窗口,以供跳转 117 | unbind-key l 118 | bind-key l choose-window 119 | 120 | bind-key k if-shell 'rm tmux/secret 2>/dev/null && exit 0 || touch tmux/secret && exit 1' { 121 | setw -g window-status-format '#[fg=green]#I-#(tmux-ui WIN #{pane_title})#[default]' 122 | } { 123 | # 但如果是隐私模式则只显示星号 124 | setw -g window-status-format '#[fg=green]#I-****#[default]' 125 | } 126 | 127 | bind-key K if-shell 'rm tmux/secret 2>/dev/null && exit 0 || touch tmux/secret && exit 1' { 128 | setw -g window-status-format '#[fg=green]#I-#(tmux-ui WIN #{pane_title})#[default]' 129 | setw -g window-status-current-format '#[fg=brightyellow]#I-#(tmux-ui WIN #{pane_title})#[default]' 130 | set -g pane-border-format '#(tmux-ui PANE #{pane_title} #{pane_active} #{pane_width})' 131 | } { 132 | # 但如果是隐私模式则只显示星号 133 | setw -g window-status-format '#[fg=green]#I-****#[default]' 134 | setw -g window-status-current-format '#[fg=brightyellow]#I-****#[default]' 135 | set -g pane-border-format '' 136 | } 137 | # }}} 138 | 139 | ########## 面板管理 ---{{{ 140 | # 141 | # 显示面板编号,方便跳转,以及通过样式得知哪个是当前面板 142 | bind-key m display-pane 143 | bind-key C-m display-pane 144 | 145 | set -g display-panes-time 3000 146 | set -g display-panes-active-colour brightblack 147 | set -g display-panes-colour brightyellow 148 | 149 | # 通过 | 和 - 来切分窗口 150 | unbind % 151 | unbind '"' 152 | bind | split-window -h -c '#{pane_current_path}' 153 | bind - split-window -v -c '#{pane_current_path}' 154 | 155 | bind-key o last-pane 156 | bind-key C-o last-pane 157 | bind-key w select-pane -t :.+ 158 | bind-key Tab select-pane -t :.+ 159 | # }}} 160 | --------------------------------------------------------------------------------