Compare commits
347 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9101cd189c | ||
|
|
196e3efec7 | ||
|
|
7adce5e2af | ||
|
|
c9aa049874 | ||
|
|
58d6603946 | ||
|
|
bfd9f68805 | ||
|
|
44c2b77595 | ||
|
|
5148d76f9f | ||
|
|
45bfc395be | ||
|
|
8052814028 | ||
|
|
1eeb2483bf | ||
|
|
2aabeb378c | ||
|
|
7054d37b58 | ||
|
|
64b20aa659 | ||
|
|
42bb664add | ||
|
|
97ed3d2da5 | ||
|
|
e2ac5e448b | ||
|
|
2365458b78 | ||
|
|
c807c1e54f | ||
|
|
6d647e8010 | ||
|
|
1dce65d146 | ||
|
|
8ceb60329d | ||
|
|
f240c7e98b | ||
|
|
f5c17768f4 | ||
|
|
d08e1c4567 | ||
|
|
e7d94ee4fb | ||
|
|
89dfc37551 | ||
|
|
02f45397ce | ||
|
|
82e377f39a | ||
|
|
7227be3189 | ||
|
|
cad04a4725 | ||
|
|
a2d6b88cde | ||
|
|
81ab3e685d | ||
|
|
c43e833fbd | ||
|
|
3d39cce181 | ||
|
|
9bbcf8c3ed | ||
|
|
669f214bd8 | ||
|
|
72c1a249e6 | ||
|
|
f94925d1fe | ||
|
|
9383608f68 | ||
|
|
2d7e82ceac | ||
|
|
7e4bbef888 | ||
|
|
cd12d2bc9a | ||
|
|
7ab52d0ccb | ||
|
|
c85ac4f5d9 | ||
|
|
1e4e284924 | ||
|
|
a5a1de5362 | ||
|
|
76d8a4b985 | ||
|
|
9f99aeacda | ||
|
|
20399f5a87 | ||
|
|
5e24a09ffe | ||
|
|
11ed85abe0 | ||
|
|
dcd777faca | ||
|
|
c52d82c6f2 | ||
|
|
74aa5fc572 | ||
|
|
10882a09b0 | ||
|
|
97c22055ac | ||
|
|
f38fb34c3a | ||
|
|
871a47ad26 | ||
|
|
c6823f5186 | ||
|
|
58e54c88af | ||
|
|
994612d15f | ||
|
|
5ff522123a | ||
|
|
84f1989256 | ||
|
|
cd0efcf2b6 | ||
|
|
1f5a38d9cf | ||
|
|
df83326a6f | ||
|
|
49bc1c9b3d | ||
|
|
6ce9d42ae4 | ||
|
|
7e45cd6dab | ||
|
|
003efa9c8a | ||
|
|
2ab7b11b46 | ||
|
|
bdf1e52f70 | ||
|
|
636fb5fd53 | ||
|
|
fba0dbf8e3 | ||
|
|
f0be1bae4c | ||
|
|
c81c298dfe | ||
|
|
58644a1cd2 | ||
|
|
7c2833e73b | ||
|
|
8ca935753b | ||
|
|
ab8ddc767f | ||
|
|
7607e3d4b6 | ||
|
|
1504da67f6 | ||
|
|
d6d8560d23 | ||
|
|
325f3b54a6 | ||
|
|
bfe174c242 | ||
|
|
4747d88f40 | ||
|
|
af1878c3a0 | ||
|
|
a365d20fd8 | ||
|
|
8c0350b16c | ||
|
|
c384d31827 | ||
|
|
8533d52c7a | ||
|
|
c08003a436 | ||
|
|
e9598d407b | ||
|
|
0e7c5358e2 | ||
|
|
5da87f7b22 | ||
|
|
6e9ff7fb34 | ||
|
|
7f149d56f0 | ||
|
|
bfcadc0691 | ||
|
|
d6ad68d537 | ||
|
|
be98a7ab47 | ||
|
|
469c70d1d7 | ||
|
|
099275c5a3 | ||
|
|
419f5d92b4 | ||
|
|
05ca945145 | ||
|
|
543cbafd0b | ||
|
|
131379e39e | ||
|
|
150754a74b | ||
|
|
d4c65ed690 | ||
|
|
295144758d | ||
|
|
ad63aaad44 | ||
|
|
b05e952268 | ||
|
|
193257c25b | ||
|
|
111c4f2677 | ||
|
|
84c5216b8f | ||
|
|
0921396c57 | ||
|
|
8b76a28458 | ||
|
|
fc0075831a | ||
|
|
4f0182621a | ||
|
|
9e4ea4a474 | ||
|
|
8ca2720ec1 | ||
|
|
c7137954fe | ||
|
|
b410832a81 | ||
|
|
da17490dbc | ||
|
|
371ed4efff | ||
|
|
e51dd82bdb | ||
|
|
66567fa595 | ||
|
|
92db70159f | ||
|
|
11cbf2a475 | ||
|
|
3367b58e39 | ||
|
|
8f7068c55b | ||
|
|
c28182c5a2 | ||
|
|
7bc946934a | ||
|
|
476ec0f0fe | ||
|
|
f8f67e61fa | ||
|
|
e9f24b4ae0 | ||
|
|
503c76ed34 | ||
|
|
307cb71023 | ||
|
|
e6bfdbba6a | ||
|
|
9275802466 | ||
|
|
ed01c6a54b | ||
|
|
ddfac919cb | ||
|
|
33e5ea5bdf | ||
|
|
2559879aaa | ||
|
|
630e56651b | ||
|
|
fb0ae8b080 | ||
|
|
203f4bf34f | ||
|
|
be1ba7229c | ||
|
|
37e6b16656 | ||
|
|
38233b39c2 | ||
|
|
d095179d6c | ||
|
|
1e583e21cc | ||
|
|
75d5aad0b6 | ||
|
|
b5eb5dc0b9 | ||
|
|
431eced76a | ||
|
|
ac1f4c85b5 | ||
|
|
ee8758e63b | ||
|
|
76e114c9f2 | ||
|
|
bb16165c51 | ||
|
|
78e994402e | ||
|
|
67ab800205 | ||
|
|
afb04b0d80 | ||
|
|
a5b572023b | ||
|
|
380e4284ee | ||
|
|
16ff5a6f54 | ||
|
|
cced80991f | ||
|
|
5fabbaf7ab | ||
|
|
215dc42294 | ||
|
|
314916279a | ||
|
|
dbf1a1bb36 | ||
|
|
8f2b8dd013 | ||
|
|
8039852aa3 | ||
|
|
d3b567953d | ||
|
|
5064ee936a | ||
|
|
86da9b0b29 | ||
|
|
acac203e42 | ||
|
|
cdd1c8cb61 | ||
|
|
439753eb6d | ||
|
|
e30700de66 | ||
|
|
994d1723b6 | ||
|
|
2d40a89c55 | ||
|
|
802cc2b5f5 | ||
|
|
2ffca90d29 | ||
|
|
ec90df9c58 | ||
|
|
8f2c2dc08f | ||
|
|
cad10c4d5c | ||
|
|
aa3686ed0b | ||
|
|
3850b63cec | ||
|
|
a7df8a4f49 | ||
|
|
7071b13e41 | ||
|
|
2324ae7f9e | ||
|
|
402163c33d | ||
|
|
3356e42e71 | ||
|
|
205e263255 | ||
|
|
aab9823a06 | ||
|
|
0245e7d1af | ||
|
|
3f800c4a68 | ||
|
|
31e901ab6d | ||
|
|
94a08d86a0 | ||
|
|
bcc7bedbc9 | ||
|
|
a878a4f3f9 | ||
|
|
238c01baf4 | ||
|
|
5a4bdd343c | ||
|
|
d916502f58 | ||
|
|
1791365e0f | ||
|
|
de2a86c859 | ||
|
|
5aeed01e38 | ||
|
|
6ab1af99da | ||
|
|
c7b2d218ef | ||
|
|
01ae462b72 | ||
|
|
eb603e7ee9 | ||
|
|
b67b29def5 | ||
|
|
d22d9b603b | ||
|
|
16b0a36c86 | ||
|
|
313f48d4ce | ||
|
|
db82cbcfae | ||
|
|
eed47a9064 | ||
|
|
9945d37df8 | ||
|
|
4e743128e9 | ||
|
|
2ed35db216 | ||
|
|
df0c7f8b46 | ||
|
|
9fe6ba4483 | ||
|
|
dee8651e61 | ||
|
|
948a9de667 | ||
|
|
d55437ddf3 | ||
|
|
98fd8c13d4 | ||
|
|
04a92fe0c1 | ||
|
|
5754b60ca0 | ||
|
|
dc1a712f52 | ||
|
|
7a6d3e1b68 | ||
|
|
970fb3155f | ||
|
|
0eb78e2d9e | ||
|
|
7d42870abd | ||
|
|
c7b9e0eee3 | ||
|
|
c5089881b3 | ||
|
|
9c1406f75d | ||
|
|
92226483be | ||
|
|
21ecfeef17 | ||
|
|
d5be9130aa | ||
|
|
f61fee829a | ||
|
|
0d684c1400 | ||
|
|
b5871c7be2 | ||
|
|
f9b328b432 | ||
|
|
830df4e5ca | ||
|
|
6f68f4edfd | ||
|
|
a7931c3e4d | ||
|
|
799cb97564 | ||
|
|
851b57e8ef | ||
|
|
ceb2866aa1 | ||
|
|
11c1e4655e | ||
|
|
aca9cbf91d | ||
|
|
08b089d9be | ||
|
|
8b254a294e | ||
|
|
7004de6e71 | ||
|
|
738e1b24e6 | ||
|
|
5a4200cc8e | ||
|
|
cb0320a471 | ||
|
|
058f034c2b | ||
|
|
9c54f3a8ea | ||
|
|
ee8cb545e1 | ||
|
|
64bbff462d | ||
|
|
f63f7e13d4 | ||
|
|
db8666fc71 | ||
|
|
93ea66e6fe | ||
|
|
7131e39c96 | ||
|
|
b80e9a3310 | ||
|
|
03802666ef | ||
|
|
ecd155f2bb | ||
|
|
d6147d5191 | ||
|
|
7ef471f1e2 | ||
|
|
c3404d296a | ||
|
|
c609bcf2ee | ||
|
|
0aee9d7c3f | ||
|
|
a49c1703ae | ||
|
|
c145a53688 | ||
|
|
c744935dd0 | ||
|
|
90b44c3a8b | ||
|
|
60a44252ea | ||
|
|
38eecd0db7 | ||
|
|
e320bafa8b | ||
|
|
754e5e61f2 | ||
|
|
afba85827a | ||
|
|
8cd5211b8b | ||
|
|
2d7525f2b4 | ||
|
|
7adb25888f | ||
|
|
b46912afcb | ||
|
|
ed3d4823b2 | ||
|
|
b8a360917e | ||
|
|
26dbe36a15 | ||
|
|
396447519d | ||
|
|
83e436bbd2 | ||
|
|
d307eff71f | ||
|
|
b935725096 | ||
|
|
12248acd7b | ||
|
|
07eb0f5e8e | ||
|
|
cfb5fc25c3 | ||
|
|
3816e1c5bd | ||
|
|
c9d551cd39 | ||
|
|
95bf0473f3 | ||
|
|
a9d92b78a2 | ||
|
|
b8ebace039 | ||
|
|
9223957931 | ||
|
|
5cddb7f2b6 | ||
|
|
08e7a3af16 | ||
|
|
1416cd432d | ||
|
|
2d6cf07a65 | ||
|
|
0b73d6112a | ||
|
|
b742ac0591 | ||
|
|
4546fd44ff | ||
|
|
216fa9341b | ||
|
|
1d6d5dcc94 | ||
|
|
61f451694f | ||
|
|
c681f0bd82 | ||
|
|
cbc0b2707e | ||
|
|
36d0f8a2a0 | ||
|
|
301f65ce16 | ||
|
|
f1b95fe45e | ||
|
|
3091d747e6 | ||
|
|
f4062e5ebb | ||
|
|
a8807f1ef0 | ||
|
|
10945927c1 | ||
|
|
ab25f698da | ||
|
|
b30e03ffb3 | ||
|
|
82157c7bd1 | ||
|
|
eb10f02ac3 | ||
|
|
b1ef94ef60 | ||
|
|
f89392b476 | ||
|
|
32c4f2239d | ||
|
|
404c00c2bf | ||
|
|
771b69d151 | ||
|
|
37a9a4121f | ||
|
|
4731c0c60d | ||
|
|
65111ae209 | ||
|
|
2a460de911 | ||
|
|
cee82a08ba | ||
|
|
387437cada | ||
|
|
af14da987e | ||
|
|
389b3ad2a4 | ||
|
|
5f1f5b7dcd | ||
|
|
7e3be4c45d | ||
|
|
c82881e6f2 | ||
|
|
6be1fabc55 | ||
|
|
4d480660b5 | ||
|
|
1f4ce98998 | ||
|
|
605f2e212e | ||
|
|
188eb74a70 | ||
|
|
2d7d113458 |
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
15
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
15
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project, such as a missing gameplay feature
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
11
.github/pull_request_template.md
vendored
Normal file
11
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
## Description
|
||||
<!-- Describe your changes in detail -->
|
||||
|
||||
## Checklist before requesting a review
|
||||
<!-- Mark with "x" inside the square brackets -->
|
||||
- [ ] I have performed a self-review of my code
|
||||
- [ ] I have tested my changes
|
||||
- [ ] I have added unit tests that prove my changes work
|
||||
|
||||
## Screenshots
|
||||
<!-- If applicable, add screenshots to help explain your changes -->
|
||||
6
.github/workflows/run-build.yml
vendored
6
.github/workflows/run-build.yml
vendored
@@ -11,10 +11,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up JDK 17
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
distribution: 'corretto'
|
||||
- name: Build with Maven (compile -> test -> package)
|
||||
run: mvn -B package --file pom.xml
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
/logs/**
|
||||
.idea/
|
||||
.idea/*
|
||||
!.idea/codeStyles/
|
||||
*.iml
|
||||
/target
|
||||
|
||||
@@ -16,3 +17,10 @@
|
||||
.project
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# Database
|
||||
database/docker-db-data
|
||||
database/docker-pg-db-data
|
||||
|
||||
# macOS files
|
||||
.DS_Store
|
||||
|
||||
8
.idea/codeStyles/Project.xml
generated
Normal file
8
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
|
||||
</JavaCodeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
19
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
19
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
wrapperVersion=3.3.2
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
||||
14
Dockerfile
14
Dockerfile
@@ -4,15 +4,21 @@
|
||||
#
|
||||
# Cosmic JAR creation stage
|
||||
#
|
||||
FROM maven:3.8.4-openjdk-17 AS jar
|
||||
FROM maven:3.9.6-amazoncorretto-21 AS jar
|
||||
|
||||
# Build in a separated location which won't have permissions issues.
|
||||
WORKDIR /opt/cosmic
|
||||
|
||||
# Any changes to the pom will affect the entire build, so it should be copied first.
|
||||
COPY pom.xml ./pom.xml
|
||||
|
||||
# Grab all the dependencies listed in the pom early, since it prevents changes to source code from requiring a complete re-download.
|
||||
# Skip compiling tests since we don't want all the dependecies to be downloaded.
|
||||
RUN mvn -f ./pom.xml clean dependency:go-offline -Dmaven.test.skip -T 1C
|
||||
# Skip compiling tests since we don't want all the dependencies to be downloaded.
|
||||
# RUN mvn -f ./pom.xml clean dependency:go-offline -Dmaven.test.skip -T 1C
|
||||
# TODO: The above command stopped working as of Java 21 upgrade due to:
|
||||
# Failed to execute goal org.apache.maven.plugins:maven-dependency-plugin:3.6.1:go-offline (default-cli) on project Cosmic: org.eclipse.aether.resolution.DependencyResolutionException: The following artifacts could
|
||||
# not be resolved: io.netty:netty-tcnative:jar:${os.detected.classifier}:2.0.65.Final (absent): Could not find artifact io.netty:netty-tcnative:jar:${os.detected.classifier}:2.0.65.Final in central (https://repo.maven.apache.org/maven2) -> [Help 1]
|
||||
|
||||
# Source code changes may not change dependencies, so it can go last.
|
||||
# Skip compiling tests since we don't want all the dependecies to be downloaded for plugins.
|
||||
COPY src ./src
|
||||
@@ -21,7 +27,7 @@ RUN mvn -f ./pom.xml clean package -Dmaven.test.skip -T 1C
|
||||
#
|
||||
# Server creation stage
|
||||
#
|
||||
FROM openjdk:17.0.2
|
||||
FROM amazoncorretto:21
|
||||
|
||||
# Host the server in a location that won't have permissions issues.
|
||||
WORKDIR /opt/server
|
||||
|
||||
345
README.md
345
README.md
@@ -1,298 +1,155 @@
|
||||
# Cosmic - MapleStory v83 server
|
||||
# Cosmic
|
||||
Cosmic is a server emulator for Global MapleStory (GMS) version 83.
|
||||
|
||||
## Introduction
|
||||
Cosmic launched as a successor to HeavenMS on March 21st 2021.
|
||||
|
||||
HeavenMS is archived, ie. it receives no further updates. This project aims to continue its development; mainly by improving code quality and make getting into PS development as easy as possible.
|
||||
Cosmic launched on March 2021. It is based on code from a long line of server emulators spanning over a decade - starting with OdinMS (2008) and ending with HeavenMS (2019).
|
||||
|
||||
This is an open source project. Anyone may contribute by opening a pull request.
|
||||
This is mainly a Java based project, but there are also a bunch of scripts written in JavaScript.
|
||||
|
||||
Only the server side is maintained. The client is directly copied from HeavenMS.
|
||||
Head developer and maintainer: __Ponk__.\
|
||||
Contributors: a lot of people over the years, and hopefully more to come. Big thanks to everyone who has contributed so far!
|
||||
|
||||
Join the Discord server where most of the discussions take place: https://discord.gg/JU5aQapVZK
|
||||
|
||||
Beware - ***This server emulator is not production ready.***
|
||||
It can be useful for testing things locally or for trying out ideas, but launching a new private server based on this and opening it up to the public
|
||||
without knowing what you're doing is not recommended.
|
||||
### Goals
|
||||
What we are working towards.
|
||||
* __Vanilla gameplay__ - stay as close to the original game as possible (within reason).
|
||||
* __Ease of use__ - getting started should be frictionless and contributing to the project straightforward.
|
||||
* __Reduce technical debt__ - making changes should be easy without causing unintended side effects.
|
||||
* __Modern tools & technologies__ - stay appealing by continuously improving the code and the project as a whole.
|
||||
|
||||
### Non-goals
|
||||
Explicitly excluded from the scope of the project.
|
||||
* __Custom gameplay features__ - existing custom features will be removed over time and new ones are unlikely to be added.
|
||||
* __Client development__ - this project is focused on the server. Please go elsewhere for client related questions.
|
||||
* __Public server__ - there will not be an official Cosmic server open to the public. Feel free to launch your own server __at your own risk__. No support will be provided.
|
||||
|
||||
## Development information
|
||||
## Project setup
|
||||
|
||||
### Status (updated 2022-10-16)
|
||||
### Contribute
|
||||
You may contribute to the project in various ways, mainly through GitHub:
|
||||
* Providing improvements to the code through a [Pull Request](https://github.com/P0nk/Cosmic/pulls) from your own fork.
|
||||
* Reporting a bug by creating an [Issue](https://github.com/P0nk/Cosmic/issues).
|
||||
* Providing information to existing issues or reviewing pull requests that others have made.
|
||||
* ...and in other ways that I haven't thought of!
|
||||
|
||||
Development is currently <span style="color:Yellow">**sporadic**</span>.
|
||||
### Continuous integration
|
||||
A GitHub Actions pipeline is set up to run the build automatically when a new pull request is opened or commits are pushed to an existing one. This ensures that the code compiles and all the tests pass.
|
||||
|
||||
My time is very limited nowadays, but I try to keep up with the submitted pull requests. I may submit some stuff of my own, once in a while.
|
||||
Once a pull request is merged, a tag with the new version is automatically created.
|
||||
|
||||
### Ways to contribute
|
||||
|
||||
* Submit a Pull Request (fork -> commit -> PR). If you don't know where to start, have a look at the issues on GitHub.
|
||||
* Report a bug (preferably as an Issue on GitHub, as reports on Discord may be forgotten or lost)
|
||||
* Spread the word about Cosmic
|
||||
|
||||
### Working with GitHub
|
||||
|
||||
Anyone with a GitHub account can contribute by making some changes in a branch and opening up a PR.
|
||||
|
||||
All activity on the GitHub repo (opening PR, commenting, creating issue, etc.) is automatically pushed (via webhook) to a public Discord channel for visibility.
|
||||
|
||||
Issues is the main place where bugs, issues or general improvements are tracked. Feel free to submit a new issue, but please keep it in English. By providing a good description, you increase the chance of a bug being fixed.
|
||||
|
||||
Tasks (past, present and future) are kept in the Cosmic project, which you get to via the "Projects" tab. This gives you an idea of where the project is moving.
|
||||
### Discord integration
|
||||
Most GitHub activity is pushed to a Discord channel for visibility. This works by leveraging a webhook. The activity includes (but is not limited to): merged commits, created PRs, comments, and new tags.
|
||||
|
||||
### Versioning
|
||||
|
||||
The project follows the [SemVer](https://semver.org/) versioning scheme using git tags.
|
||||
As a pull request gets merged, a new version is automatically created.
|
||||
|
||||
Bug fixes result in bumped patch version: 1.2.__3__ -> 1.2.__4__
|
||||
|
||||
General improvements result in bumped minor version: 1.__2__.3 -> 1.__3__.3
|
||||
|
||||
Major changes result in bumped major version: __1__.2.3 -> __2__.2.3
|
||||
|
||||
### Cosmic
|
||||
|
||||
- GitHub: https://github.com/P0nk/Cosmic
|
||||
- Discord: https://discord.gg/JU5aQapVZK
|
||||
|
||||
### HeavenMS
|
||||
- GitHub: https://github.com/ronancpl/HeavenMS
|
||||
- Discord: https://discord.gg/Q7wKxHX
|
||||
|
||||
## Tools / downloads
|
||||
* **Java 17 SDK** - Needed to compile and run Java code. Install manually or through IntelliJ depending on how you prefer to launch the server. Not required for launching with Docker.
|
||||
* Link: https://jdk.java.net/17/
|
||||
|
||||
|
||||
* **IntelliJ IDEA** - Java IDE and your main tool for working with the source code. Community edition is good enough.
|
||||
* Link: https://www.jetbrains.com/idea/
|
||||
|
||||
|
||||
* **MySQL Community Server 8** - Database for game data.
|
||||
* Link: https://dev.mysql.com/downloads/mysql/
|
||||
|
||||
|
||||
* **MySQL Workbench 8** - Client for interacting with the database. Other clients do exist.
|
||||
* Link: https://dev.mysql.com/downloads/workbench/
|
||||
|
||||
|
||||
* **Docker Desktop** (optional) - For launching the game locally with less hassle.
|
||||
* Link: https://www.docker.com/products/docker-desktop
|
||||
|
||||
|
||||
* **Client files and general tools**
|
||||
* Link: https://drive.google.com/drive/folders/1hgnb92MGL6xqEp9szEMBh0K9pSJcJ6IT?usp=sharing
|
||||
* This is Ponk's own Google Drive, similar to how Ronan provides files for HeavenMS.
|
||||
|
||||
|
||||
### MapleStory client
|
||||
|
||||
- Latest localhost client: https://hostr.co/amuX5SLeeVZx
|
||||
|
||||
**Important note about localhost clients**: these executables are red-flagged by antivirus tools as __potentially malicious software__,
|
||||
this happens due to the reverse engineering methods that were applied onto these software artifacts.
|
||||
Those depicted here have been put to use for years already and posed no harm so far, so they are soundly assumed to be safe.
|
||||
|
||||
The project follows the [semantic versioning](https://semver.org/) scheme using git tags.
|
||||
* *Bug fixes* are treated as PATCH: 1.2.__3__ -> 1.2.__4__
|
||||
* *General changes or improvements* are treated as MINOR: 1.__2__.3 -> 1.__3.0__
|
||||
* *Major changes* are treated as MAJOR: __1__.2.3 -> __2.0.0__
|
||||
|
||||
## Getting started
|
||||
The localhost MapleStory client needs to be installed, as well as the server that will host the game.
|
||||
Follow along as I go through the steps to play the game on your local computer from start to finish. I won't go into extreme detail, so if you don't have prior experience with Java or git, you might struggle.
|
||||
|
||||
### Installing the client
|
||||
We will set up the following:
|
||||
- Database - the database is used by the server to store game data such as accounts, characters and inventory items.
|
||||
- Server - the server is the "brain" and routes network traffic between the clients.
|
||||
- Client - the client is the application used to _play the game_, i.e. MapleStory.exe.
|
||||
|
||||
1. Install MapleStory with "MapleGlobal-v83-setup.exe" in your folder of choice (e.g. "C:\Nexon\MapleStory") and follow their instructions.
|
||||
2. Once done, erase these files: "HShield" (folder), "ASPLnchr.exe", "MapleStory.exe" and "Patcher.exe".
|
||||
3. Extract into the client folder the "HeavenMS-localhost-WINDOW.exe" (from now on referred to as "localhost.exe") from the provided link.
|
||||
4. Overwrite the original WZ files with the ones provided on the Google Drive: "CosmicWZ-v1-2021.05.10.zip"
|
||||
- This is currently identical to the latest HeavenMS WZ files (except for the file name): "commit397_wz-20210321T173600Z-001.zip"
|
||||
### 1 - Database
|
||||
You will start by installing the database server and database client. Then you will connect to the server with the client to create a new database schema.
|
||||
|
||||
#### Editing localhost IP target
|
||||
#### Steps
|
||||
|
||||
If you are not using "localhost" as the target IP on the server's config file, you will need to HEX-EDIT localhost.exe to fetch your IP. Track down all IP locations by searching for "Type: String" "127.0.0.1", and applying the changes wherever it fits.
|
||||
1. Download and install [MySQL Community Server 8+](https://dev.mysql.com/downloads/mysql/). You will have to set a root password. Make sure you don't lose it because you will need it later.
|
||||
2. Download and install [HeidiSQL](https://www.heidisql.com/download.php).
|
||||
3. Connect to the database:
|
||||
1. Open HeidiSQL
|
||||
2. Create a new Session: "New" -> fill in your password -> "Save"
|
||||
3. Connect to the database: click on your saved session -> "Open"
|
||||
4. Create a new database schema:
|
||||
1. In the opened session, right-click on the session name in the menu on the left
|
||||
2. "Create new" -> "Database" -> database name should be "cosmic" -> "OK"
|
||||
5. Done. The database is now ready. Once the Cosmic server starts, it will create tables and populate some of them with initial data.
|
||||
|
||||
To hex-edit, install the Neo Hex Editor from "free-hex-editor-neo.exe" and follow their instructions. Once done, open localhost.exe for editing and overwrite the IP values under the 3 addresses. Save the changes and exit the editor.
|
||||
### 2 - Server
|
||||
You will start by cloning the repository, then configure the database properties and lastly start the server.
|
||||
|
||||
(TODO: find suitable alternative to Neo Hex Editor)
|
||||
#### Prerequisites
|
||||
* Java 21 (I recommend [Amazon Corretto](https://aws.amazon.com/corretto))
|
||||
* IDE (I recommend [IntelliJ IDEA](https://www.jetbrains.com/idea/))
|
||||
|
||||
#### Testing the localhost
|
||||
#### Steps
|
||||
|
||||
Open the "localhost.exe" client.
|
||||
If by any means the program did not open, and checking the server log your ping has been listened by the server
|
||||
and you are using Windows 8, 10 or 11, it is probably some compatibility issue.
|
||||
1. Clone Cosmic into a new project. In IntelliJ, you would create a new project from version control.
|
||||
2. Open _config.yaml_. Find "DB_PASS" and set it to your database root user password.
|
||||
3. Start the server. The main method is located in `net.server.Server`.
|
||||
4. If you see "Cosmic is now online" in the console, it means the server is online and ready to serve traffic. Yay!
|
||||
|
||||
In some cases it helps to spam click the exe a few times (2-3 times usually works for me on W10).
|
||||
Below, I list other ways of running the server which are completely optional.
|
||||
|
||||
In that case, extract "lolwut.exe" from "lolwut-v0.01.rar" and place it on the MapleStory client folder ("C:\Nexon\MapleStory").
|
||||
Your "localhost.exe" property settings must follow these:
|
||||
#### Docker
|
||||
Support for Docker is also provided out of the box, as an alternative to running straight in the IDE. If you have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed it's as easy as running `docker compose up`.
|
||||
|
||||
Note: "lolwut.exe" is currently not available in the Google Drive.
|
||||
Making changes becomes a bit more tedious though as you have to rebuild the server image via `docker compose up --build`.
|
||||
|
||||
* Run in compatibility mode: Windows 7;
|
||||
* Unchecked reduced color mode;
|
||||
* 640 x 480 resolution;
|
||||
* Unchecked disable display on high DPI settings;
|
||||
* Run as an administrator;
|
||||
* Opening "lolwut.exe", use Fraysa's method.
|
||||
#### Jar
|
||||
Another option is to start the server from a terminal by running a jar file. You first need to build the jar file from source which requires [Maven](https://maven.apache.org/). Fortunately, [Maven Wrapper](https://maven.apache.org/wrapper/) is provided so you don't have to install Maven separately.
|
||||
|
||||
Important: should the client be refused a connection to the game server, it may be because of firewall issues. Head to the end of this file to proceed in allowing this connection through the computer's firewall. Alternatively, one can deactivate the firewall and try opening the client again.
|
||||
You can also search the server logs (/logs/cosmic-log.log) if any connection attempts have been made to ease debugging.
|
||||
Building the jar file is as easy as running ``./mvnw.cmd clean package``. The project is configured to produce a "fat" jar which contains all dependencies (by utilizing the _maven-assembly-plugin_). Note that the WZ XML files are __not__ included in the jar.
|
||||
|
||||
---
|
||||
### Installing the server
|
||||
1. Configure the project
|
||||
2. Set up the database
|
||||
3. Launch the server
|
||||
To run the jar, a ``launch.bat`` file is provided for convenience. Simply double-click it and the server will start in a new terminal window.
|
||||
|
||||
If you are using Docker (quick start):
|
||||
1. Configure the project
|
||||
2. Launch the server
|
||||
Alternatively, run the jar file from the terminal. Just remember to provide the `wz-path` system property pointing to your wz directory.
|
||||
|
||||
#### Configuring the project
|
||||
### 3 - Client
|
||||
The client files are located in a separate repository: https://github.com/P0nk/Cosmic-client
|
||||
|
||||
The easiest way to set up your project is to clone the repository directly into a new IntelliJ project.
|
||||
Follow the installation guide in the README.
|
||||
|
||||
1. Install IntelliJ
|
||||
2. Create a new "Project from Version Control..."
|
||||
3. Enter the URL to this GitHub repository: "https://github.com/P0nk/Cosmic.git"
|
||||
4. Click on "Clone". A new project will now be created with all the files from the repository.
|
||||
### 4 - Getting into the game
|
||||
You have successfully started the client, and you're looking at the login screen.
|
||||
|
||||
#### Setting up the database
|
||||
|
||||
1. Install MySQL Server 8 and MySQL Workbench 8.
|
||||
2. Using Workbench, create a new user with username "cosmic_server" and password "snailshell".
|
||||
This the default configuration in Cosmic.
|
||||
* (Optional) Restrict the Schema Privileges for this new user for improved security.
|
||||
Add a new entry with "Schemas matching pattern: cosmic" and only select "SELECT", "INSERT", "UPDATE", "DELETE" under "Object Rights"
|
||||
3. Run the sql scripts in the "database/sql" directory of the project in the order indicated by their names.
|
||||
* Make sure you are connected to the database with the "root" user to be able to run the scripts.
|
||||
* Run scripts one by one through the menu: "File" -> "Run SQL Script" -> select the script file to run -> "Run"
|
||||
* The 3rd script "3-db_shopupdate" is optional. It adds custom shop items for certain NPCs.
|
||||
* The 4th script "4-db_admin" is also optional, but recommended if you are new. It adds an admin account to simplify the setup.
|
||||
|
||||
Use this info when you connect to MySQL Server for the first time:
|
||||
* Server Host: localhost
|
||||
* Port: 3306
|
||||
* Username: root
|
||||
* Password: <whatever password you set during MySQL Server installation>
|
||||
|
||||
At the end of the execution of these sql scripts, you should have installed a database schema named "cosmic".
|
||||
REGISTER YOUR FIRST ACCOUNT to be used in-game by **manually creating** an entry in the table "accounts" in the database with a username and password.
|
||||
|
||||
|
||||
### Running the server
|
||||
|
||||
Configure the IP you want to use for your MapleStory server in "config.yaml" file, or set it as "localhost" if you want to run it only on your machine.
|
||||
Alternatively, you can use the IP given by Hamachi to use on a Hamachi network, or you can use a non-Hamachi method of port-forwarding. Neither will be approached here.
|
||||
|
||||
|
||||
To launch the server, you may either:
|
||||
* Launch inside IntelliJ
|
||||
* Launch a built jar file
|
||||
* Launch with Docker
|
||||
|
||||
#### Run inside IntelliJ
|
||||
1. Open the file src/main/java/net/server/Server.java.
|
||||
2. Click the green arrow to the left of the class definition "public class Server", and then "Run Cosmic".
|
||||
* Alternatively (recommended), create a new Configuration that points to "net.server.Server".
|
||||
3. The server launches in a terminal window inside IntelliJ.
|
||||
|
||||
#### Run from a jar file
|
||||
1. Create the jar file
|
||||
* The jar file is created by the Maven assembly plugin in the package lifecycle.
|
||||
* If you already have Maven installed, simply run the command "mvn clean install" to create the jar file.
|
||||
* IntelliJ also comes with built-in Maven support. Open a new terminal window inside IntelliJ, type "mvn clean install" (your command should now be marked green), then Ctrl+Enter to build the jar file.
|
||||
2. Launch the jar file
|
||||
* Double click on "launch.bat" (need to have Java 17 installed)
|
||||
|
||||
#### Run as containers with Docker
|
||||
1. Start Docker
|
||||
2. Run the command "docker compose up" at the root of the project.
|
||||
* If you make any changes to the code, make sure you append the "--build" option at the end of the command to force rebuild the server image.
|
||||
|
||||
---
|
||||
### Getting into the game
|
||||
|
||||
If you ran the admin sql script, there already exists an account in your database with an admin character on it. You don't need to change its GM level. Log in using these credentials:
|
||||
#### Logging in
|
||||
At this point, you can log in to the admin account using the following credentials:
|
||||
* Username: "admin"
|
||||
* Password: "admin"
|
||||
* Pin: "0000"
|
||||
* Pic: "000000"
|
||||
|
||||
By default, the server source is set to allow AUTO-REGISTERING. This means that, by simply typing in a "Login ID" and a "Password", you're able to create a new account.
|
||||
You can also create a new regular account by typing in your desired username & password and attempting to log in. This "automatic registration" feature lets you create new accounts to play around with. It is enabled by default (see _config.yaml_).
|
||||
|
||||
After creating a character, experiment typing in all-chat "@commands".
|
||||
This will display all available commands for the current GM level your character has.
|
||||
#### Entering the game
|
||||
Create a new character as you normally would, and then select it to enter the game. Hooray, finally we're in!
|
||||
|
||||
To change a character's GM level, make sure that character is not logged in, then:
|
||||
If you log in to the "Admin" character, you'll notice that the character looks almost invisible. This is hide mode, which is enabled by default when you log in to a GM character. You won't be visible to normal players and no mobs will move if you're alone on the map. Toggle hide mode on or off by typing "@hide" in the in-game chat.
|
||||
|
||||
1. Open MySQL Workbench;
|
||||
2. Expand "cosmic" schema;
|
||||
3. Expand "Tables";
|
||||
4. Right-click "characters" and click "Select Rows"
|
||||
5. Find your character in Result Grid. Scroll to the right and find the "gm" column.
|
||||
6. Edit your character's gm value and click "Apply", and then "Apply" again in the window that appeared, then "Finish".
|
||||
* 0 is what ordinary players start with, and 6 is the highest gm value. Higher level gms have access to more commands in game.
|
||||
Hide is one of many commands available to players, type "@commands" to see the full list. Higher ranked GMs have access to more powerful commands.
|
||||
|
||||
---
|
||||
### Some notes about WZ/WZ.XML EDITING
|
||||
Brief introduction to WZ files: they are the asset/data files required by the client and server. The client can read the .wz files directly, but the server requires them in XML format.
|
||||
The server also does not make use of any of the sprites, which is where different kinds of exporting comes into the picture. HaRepacker allows you to export to Private server XML, which is the .img files packaged in the .wz stripped of sprites and converted to XML.
|
||||
That's it, have fun playing around in game!
|
||||
|
||||
Link to HaRepacker-resurrected, the standard tool for handling WZ files: https://github.com/lastbattle/Harepacker-resurrected
|
||||
## Advanced concepts
|
||||
Some slightly more advanced concepts that might be useful once you're up and running.
|
||||
|
||||
NOTE: Be extremely wary when using server-side's XMLs data being reimported into the client's WZ, as some means of synchronization between the server and client modules, this action COULD generate some kind of bugs afterwards. Client-to-server data reimporting seems to be fine, though.
|
||||
### Host on remote server
|
||||
You don't have to host the server on your local machine to play. It's possible to host on a remote server such as a VPS or a dedicated server.
|
||||
|
||||
#### Editing the v83 WZ's:
|
||||
I leave it to you to figure out the server hosting part, but once you have that running you'll need to edit the client ip to point to your remote server ip.
|
||||
|
||||
### WZ files
|
||||
WZ files are the asset/data files required by the client and server. Typically, the [HaRepacker-resurrected](https://github.com/lastbattle/Harepacker-resurrected) tool is used to manage (view, edit, export) the .wz files.
|
||||
|
||||
* Use the HaRepacker-resurrected 4.2.4 editor, encryption "GMS (old)".
|
||||
* Open the desired WZ for editing and use the node hierarchy to make the desired changes (copy/pasting nodes may be unreliable in rare scenarios).
|
||||
* Save the changed WZ, **overwriting the original content** at the client folder.
|
||||
* Finally, **RE-EXPORT (using the "Private Server..." exporting option) the changed XMLs into the server's WZ.XML files**, overwriting the old contents.
|
||||
The client can read the .wz files directly, but the server requires them to be in XML format. The server does not make use of the sprites, which is the motivation for different kinds of exporting.
|
||||
HaRepacker allows you to export to "Private server", which is the .img files packaged in the .wz stripped of sprites and converted to XML. This takes much less disk space.
|
||||
|
||||
**These steps are IMPORTANT, to maintain synchronization** between the server and client modules.
|
||||
This server requires custom .wz files (unfortunately), as you may have noted during installation of the client. The intention is for these to be removed eventually and to solely run on vanilla .wz files.
|
||||
|
||||
---
|
||||
### Portforwarding the SERVER
|
||||
#### WZ editing
|
||||
* Use the HaRepacker-resurrected editor, encryption "GMS (old)".
|
||||
* Open the desired .wz for editing and use the node hierarchy to make the desired changes (copy/pasting nodes may be unreliable in rare scenarios).
|
||||
* Save the changed .wz, overwriting the original content at the client folder.
|
||||
* Finally, re-export (using the "Private Server" exporting option) the changed XMLs into the server's .wz XML files (found in the "wz" directory), overwriting the old contents.
|
||||
|
||||
To use portforward, you will need to have permission to change things on the LAN router. Access your router using the Internet browser. URLs vary accordingly with the manufacturer. To discover it, open the command prompt and type "ipconfig" and search for the "default gateway" field. The IP shown there is the URL needed to access the router. Also, look for the IP given to your machine (aka "IPv4 address" field), which will be the server one.
|
||||
Make sure to always export from the client .wz files to the server XML, and not the other way around.
|
||||
|
||||
The default login/password also varies, so use the link http://www.routerpasswords.com/ as reference. Usually, login as "admin" and password as "password" completes the task well.
|
||||
|
||||
Now you have logged in the router system, search for anything related to portforwarding. Should the system prompt you between portforwarding and portriggering, pick the first, it is what we will be using.
|
||||
|
||||
Now, it is needed to enable the right ports for the Internet. For Cosmic, it is basically needed to open ports 7575 to 7575 + (number of channels) and port 8484. Create a new custom service which enables that range of ports for the server's channel and opt to use TCP/UDP protocols. Finally, create a custom service now for using port 8484.
|
||||
|
||||
Optionally, if you want to host a webpage, portforward the port 80 (the HTTP port) as well.
|
||||
|
||||
It is not done yet, sometimes the firewalls will block connections between the LAN and the Internet. To overcome this, it is needed to create some rules for the firewall to permit these connections. Search for the advanced options with firewalls on your computer and, with it open, create two rules (one outbound and one inbound).
|
||||
|
||||
These rules must target "one application", "enable connections" and must target your MapleStory client (aka localhost).
|
||||
|
||||
After all these steps, the portforwarding process should now be complete.
|
||||
|
||||
---
|
||||
|
||||
### Client changelog
|
||||
The following list, in bottom-up chronological order,
|
||||
holds information regarding all changes that were applied from the starting localhost used in this development.
|
||||
Some lines have a link attached, that will lead you to a snapshot of the localhost at that version of the artifact.
|
||||
Naturally, later versions holds all previous changes along with the proposed changes.
|
||||
|
||||
**Change log:**
|
||||
|
||||
* Fixed Monster Magnet crashing the caster when trying to pull fixed mobs, credits to Shavit. https://gofile.io/?c=BW7dVM (dead link)
|
||||
* Cleared need for administrator privileges (OS) to play the game, credits to Ubaware.
|
||||
* Set a higher cap for AP assigning with AP Reset, credits to Ubaware.
|
||||
* Fixed Monster Magnet crashing the caster when trying to pull bosses. Drawback: Dojo HPBar becomes unavailable. https://hostr.co/SvnSKrGzXhG0
|
||||
* Fixed some 'rn' problems with quest icons & removed "tab" from party leader changed message. https://hostr.co/tsYsQzzV6xT0
|
||||
* Removed block on applying attack-based strengthening gems on non-weapon equipments. https://hostr.co/m2bVtnizCtmD
|
||||
* Set a higher cap for SPEED.
|
||||
* Removed the AP assigning block for beginners below level 10. https://hostr.co/AHAHzneCti9B
|
||||
* Removed block on party for beginners level 10 or below. https://hostr.co/JZq53mMtToCz
|
||||
* Removed block on MTS entering in some maps, rendering the buyback option available.
|
||||
* Removed "AP excess" popup and limited actions on Admin/MWLB, credits to kevintjuh93.
|
||||
* Removed "You've gained a level!" popup, credits to PrinceReborn.
|
||||
* Removed caps for WATK, WDEF, MDEF, ACC, AVOID.
|
||||
* 'n' problem fixed.
|
||||
* Fraysa's https://hostr.co/gJbLZITRVHmv
|
||||
* Eric's MapleSilver starting on window-mode.
|
||||
Editing the client .wz without exporting to the server may lead to strange behavior.
|
||||
|
||||
36
config.yaml
36
config.yaml
@@ -159,10 +159,10 @@ worlds:
|
||||
|
||||
server:
|
||||
#Database Configuration
|
||||
DB_URL_FORMAT: "jdbc:mysql://%s:3306/cosmic"
|
||||
DB_HOST: "localhost"
|
||||
DB_USER: "cosmic_server"
|
||||
DB_PASS: "snailshell"
|
||||
DB_URL_FORMAT: "jdbc:mysql://%s:3306/cosmic" # If the docker ENV for DB_HOST is anything but "db", this string format should be changed from 3306 to 3307 (or whichever port it was changed to in docker)
|
||||
DB_HOST: "localhost"
|
||||
DB_USER: "root"
|
||||
DB_PASS: ""
|
||||
INIT_CONNECTION_POOL_TIMEOUT: 90 # Seconds
|
||||
|
||||
#Login Configuration
|
||||
@@ -220,7 +220,6 @@ server:
|
||||
USE_MTS: false
|
||||
USE_CPQ: true #Renders the CPQ available or not.
|
||||
USE_AUTOHIDE_GM: true #When enabled, GMs are automatically hidden when joining. Thanks to Steven Deblois (steven1152).
|
||||
USE_BUYBACK_SYSTEM: false #Enables the HeavenMS-builtin buyback system, to be used by dead players when clicking the MTS button.
|
||||
USE_FIXED_RATIO_HPMP_UPDATE: false #Enables the HeavenMS-builtin HPMP update based on the current pool to max pool ratio.
|
||||
USE_FAMILY_SYSTEM: true
|
||||
USE_DUEY: true
|
||||
@@ -234,6 +233,7 @@ server:
|
||||
USE_STARTING_AP_4: false #Use early-GMS 4/4/4/4 starting stats. To overcome AP shortage, this gives 4AP/5AP at 1st/2nd job advancements.
|
||||
USE_AUTOBAN: false #Commands the server to detect infractors automatically.
|
||||
USE_AUTOBAN_LOG: true #Log autoban related messages. Still logs even with USE_AUTOBAN disabled.
|
||||
USE_EXP_GAIN_LOG: false #Logs characters exp gains; logs world rate & coupon exp, total gained exp, and current exp, level can be calculated from "ExpTable".
|
||||
USE_AUTOSAVE: true #Enables server autosaving feature (saves characters to DB each 1 hour).
|
||||
USE_SERVER_AUTOASSIGNER: false #HeavenMS-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments.
|
||||
USE_REFRESH_RANK_MOVE: true
|
||||
@@ -249,7 +249,6 @@ server:
|
||||
USE_ENFORCE_MERCHANT_SAVE: true #Forces automatic DB save on merchant owners, at every item movement on shop.
|
||||
USE_ENFORCE_MDOOR_POSITION: false #Forces mystic door to be spawned near spawnpoints.
|
||||
USE_SPAWN_CLEAN_MDOOR: false #Makes mystic doors to be spawned without deploy animation. This clears disconnecting issues that may happen when trying to cancel doors a couple seconds after deployment.
|
||||
USE_SPAWN_LOOT_ON_ANIMATION: false #Makes loot appear some time after the mob has been killed (following the mob death animation, instead of instantly).
|
||||
USE_SPAWN_RELEVANT_LOOT: true #Forces to only spawn loots that are collectable by the player or any of their party members.
|
||||
USE_ERASE_PERMIT_ON_OPENSHOP: true #Forces "shop permit" item to be consumed when player deploy his/her player shop.
|
||||
USE_ERASE_UNTRADEABLE_DROP: true #Forces flagged untradeable items to disappear when dropped.
|
||||
@@ -257,10 +256,8 @@ server:
|
||||
USE_BUFF_MOST_SIGNIFICANT: true #When applying buffs, the player will stick with the highest stat boost among the listed, rather than overwriting stats.
|
||||
USE_BUFF_EVERLASTING: false #Every applied buff on players holds expiration time so high it'd be considered permanent. Suggestion thanks to Vcoc.
|
||||
USE_MULTIPLE_SAME_EQUIP_DROP: true #Enables multiple drops by mobs of the same equipment, number of possible drops based on the quantities provided at the drop data.
|
||||
USE_BANISHABLE_TOWN_SCROLL: false #Enables town scrolls to act as if it's a "player banish", rendering the antibanish scroll effect available.
|
||||
USE_ENABLE_FULL_RESPAWN: false #At respawn task, always respawn missing mobs when they're available. Spawn count doesn't depend on how many players are currently there.
|
||||
USE_ENABLE_CHAT_LOG: false #Write in-game chat to log
|
||||
USE_REBIRTH_SYSTEM: false #Flag to enable/disable rebirth system
|
||||
USE_MAP_OWNERSHIP_SYSTEM: false #Flag to enable/disable map ownership system
|
||||
USE_FISHING_SYSTEM: false #Flag to enable/disable custom fishing system
|
||||
USE_NPCS_SCRIPTABLE: true #Flag to enable/disable serverside predefined script NPCs.
|
||||
@@ -286,10 +283,6 @@ server:
|
||||
USE_MAKER_PERMISSIVE_ATKUP: false #Allows players to use attack-based strengthening gems on non-weapon items.
|
||||
USE_MAKER_FEE_HEURISTICS: true #Apply compiled values for stimulants and reagents into the Maker fee calculations (max error revolves around 50k mesos). Set false to use basic constant values instead (results are never higher than requested by the client-side).
|
||||
|
||||
#Custom Configuration
|
||||
USE_ENABLE_CUSTOM_NPC_SCRIPT: false #Enables usage of custom HeavenMS NPC scripts (Agent E, Coco, etc). Will not disable Abdula (it's actually useful for the gameplay) or quests.
|
||||
USE_STARTER_MERGE: false #Allows any players to use the Equipment Merge custom mechanic (as opposed to the high-level, Maker lv3 requisites).
|
||||
|
||||
#Commands Configuration
|
||||
BLOCK_GENERATE_CASH_ITEM: false #Prevents creation of cash items with the item/drop command.
|
||||
USE_WHOLE_SERVER_RANKING: false #Enables a ranking pool made from every character registered on the server for the "ranks" command, instead of separated by worlds.
|
||||
@@ -318,7 +311,6 @@ server:
|
||||
NAME_CHANGE_COOLDOWN: 2592000000 # (30*24*60*60*1000) Cooldown for name changes, default (GMS) is 30 days.
|
||||
WORLD_TRANSFER_COOLDOWN: 2592000000 # (30*24*60*60*1000) Cooldown for world tranfers, default is same as name change (30 days).
|
||||
INSTANT_NAME_CHANGE: false #Whether or not to wait for server restart to apply name changes. Does on reconnect otherwise (requires queries on every login).
|
||||
REBIRTH_NPC_ID: 9010021 #ID of the NPC that should be replaced with the rebirth mechanic, if enabled.
|
||||
|
||||
#Dangling Items/Locks Configuration
|
||||
ITEM_EXPIRE_TIME: 180000 # (3 * 60 * 1000) Time before items start disappearing. Recommended to be set up to 3 minutes.
|
||||
@@ -346,10 +338,9 @@ server:
|
||||
USE_PERFECT_SCROLLING: false #Scrolls doesn't use slots upon failure.
|
||||
USE_ENHANCED_CHSCROLL: false #Equips even more powerful with chaos upgrade.
|
||||
USE_ENHANCED_CRAFTING: false #Apply chaos scroll on every equip crafted.
|
||||
USE_ENHANCED_CLNSLATE: false #Clean slates can be applied to recover successfully used slots as well.
|
||||
SCROLL_CHANCE_ROLLS: 1 #Number of rolls for success on a scroll, set 1 for default.
|
||||
CHSCROLL_STAT_RATE: 1 #Number of rolls of stat upgrade on a successfully applied chaos scroll, set 1 for default.
|
||||
CHSCROLL_STAT_RANGE: 6 #Stat upgrade range (-N, N) on chaos scrolls.
|
||||
CHSCROLL_STAT_RANGE: 5 #Stat upgrade range (-N, N) on chaos scrolls.
|
||||
|
||||
#Beginner Skills Configuration
|
||||
USE_ULTRA_NIMBLE_FEET: false #Massive speed & jump upgrade.
|
||||
@@ -396,7 +387,6 @@ server:
|
||||
USE_EQUIPMNT_LVLUP_SLOTS: false #Equips can upgrade slots at level up.
|
||||
USE_EQUIPMNT_LVLUP_POWER: false #Enable more powerful stat upgrades at equip level up.
|
||||
USE_EQUIPMNT_LVLUP_CASH: false #Enable equip leveling up on cash equipments as well.
|
||||
USE_SPIKES_AVOID_BANISH: false #Shoes equipped with spikes prevents mobs from banishing wearer.
|
||||
MAX_EQUIPMNT_LVLUP_STAT_UP: 10000 #Max stat upgrade an equipment can have on a levelup.
|
||||
MAX_EQUIPMNT_STAT: 32767 #Max stat on an equipment by leveling up.
|
||||
USE_EQUIPMNT_LVLUP: 1 #All equips lvlup at max level of N, set 1 to disable.
|
||||
@@ -447,20 +437,18 @@ server:
|
||||
WEDDING_GIFT_LIMIT: 1 #Max number of gifts per person to same wishlist on marriage instances.
|
||||
WEDDING_BLESSER_SHOWFX: true #Pops bubble sprite effect on players blessing the couple. Setting this false shows the blessing effect on the couple instead.
|
||||
|
||||
#Buyback Configuration
|
||||
USE_BUYBACK_WITH_MESOS: true #Enables usage of either mesos or NX for the buyback fee.
|
||||
BUYBACK_FEE: 77.70 #Sets the base amount needed to buyback (level 30 or under will use the base value).
|
||||
BUYBACK_LEVEL_STACK_FEE: 85.47 #Sets the level-stacking portion of the amount needed to buyback (fee will sum up linearly until level 120, when it reaches the peak).
|
||||
BUYBACK_MESO_MULTIPLIER: 1000 #Sets a multiplier for the fee when using meso as the charge unit.
|
||||
BUYBACK_RETURN_MINUTES: 1 #Sets the maximum amount of time the player can wait before decide to buyback.
|
||||
BUYBACK_COOLDOWN_MINUTES: 7 #Sets the time the player must wait before using buyback again.
|
||||
|
||||
# Login timeout by shavit
|
||||
TIMEOUT_DURATION: 3600000 # Kicks clients who don't send any packet to the game server in due time (in millisseconds).
|
||||
|
||||
#Event End Timestamp
|
||||
EVENT_END_TIMESTAMP: 1428897600000
|
||||
|
||||
# GM Security Configuration
|
||||
MINIMUM_GM_LEVEL_TO_TRADE: 4
|
||||
MINIMUM_GM_LEVEL_TO_USE_STORAGE: 4
|
||||
MINIMUM_GM_LEVEL_TO_USE_DUEY: 4
|
||||
MINIMUM_GM_LEVEL_TO_DROP: 4
|
||||
|
||||
#Any NPC ids that should search for a js override script (useful if they already have wz entries since otherwise they're ignored).
|
||||
NPCS_SCRIPTABLE:
|
||||
#9200000: Talk to Cody # Cody
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,351 +0,0 @@
|
||||
#THIS SQL IS OPTIONAL, TO BE USED AFTER 'db_drops.sql'
|
||||
#THIS REQUIRES PROVIDED WZ FILES
|
||||
|
||||
USE `cosmic`;
|
||||
|
||||
# Scroll shop at Asia, chair shop at Kino Konoko, potion shop at T-1337
|
||||
INSERT INTO `shops` (`shopid`,`npcid`) VALUES
|
||||
(2082014,2082014),
|
||||
(9110002,9110002),
|
||||
(9201101,9201101);
|
||||
|
||||
INSERT IGNORE INTO `shopitems` (`shopid`, `itemid`, `price`, `pitch`, `position`) VALUES
|
||||
(2082014, 2040004, 480000, 0, 1),
|
||||
(2082014, 2040025, 500000, 0, 2),
|
||||
(2082014, 2040029, 500000, 0, 3),
|
||||
(2082014, 2040017, 500000, 0, 4),
|
||||
(2082014, 2040301, 400000, 0, 5),
|
||||
(2082014, 2040317, 400000, 0, 6),
|
||||
(2082014, 2040321, 400000, 0, 7),
|
||||
(2082014, 2040413, 400000, 0, 8),
|
||||
(2082014, 2040418, 400000, 0, 9),
|
||||
(2082014, 2040501, 250000, 0, 10),
|
||||
(2082014, 2040513, 250000, 0, 11),
|
||||
(2082014, 2040516, 250000, 0, 12),
|
||||
(2082014, 2040532, 250000, 0, 13),
|
||||
(2082014, 2040613, 400000, 0, 14),
|
||||
(2082014, 2040701, 450000, 0, 15),
|
||||
(2082014, 2040704, 450000, 0, 16),
|
||||
(2082014, 2040707, 450000, 0, 17),
|
||||
(2082014, 2040804, 550000, 0, 18),
|
||||
(2082014, 2040817, 550000, 0, 19),
|
||||
(2082014, 2040914, 480000, 0, 20),
|
||||
(2082014, 2040919, 480000, 0, 21),
|
||||
(2082014, 2041007, 470000, 0, 22),
|
||||
(2082014, 2041010, 470000, 0, 23),
|
||||
(2082014, 2041013, 500000, 0, 24),
|
||||
(2082014, 2041016, 500000, 0, 25),
|
||||
(2082014, 2041019, 500000, 0, 26),
|
||||
(2082014, 2041022, 500000, 0, 27),
|
||||
(2082014, 2044901, 520000, 0, 28),
|
||||
(2082014, 2044701, 520000, 0, 29),
|
||||
(2082014, 2043001, 520000, 0, 30),
|
||||
(2082014, 2043801, 520000, 0, 31),
|
||||
(2082014, 2044601, 520000, 0, 32),
|
||||
(2082014, 2040727, 50000, 0, 33),
|
||||
(2082014, 2041058, 50000, 0, 34),
|
||||
(2082014, 2040807, 1000000, 0, 35),
|
||||
(2082014, 2040005, 22000, 0, 36),
|
||||
(2082014, 2040026, 23000, 0, 37),
|
||||
(2082014, 2040031, 23000, 0, 38),
|
||||
(2082014, 2040016, 23000, 0, 39),
|
||||
(2082014, 2040302, 25000, 0, 40),
|
||||
(2082014, 2040318, 25000, 0, 41),
|
||||
(2082014, 2040323, 25000, 0, 42),
|
||||
(2082014, 2040412, 20000, 0, 43),
|
||||
(2082014, 2040419, 20000, 0, 44),
|
||||
(2082014, 2040502, 25000, 0, 45),
|
||||
(2082014, 2040514, 25000, 0, 46),
|
||||
(2082014, 2040517, 25000, 0, 47),
|
||||
(2082014, 2040534, 25000, 0, 48),
|
||||
(2082014, 2040612, 20000, 0, 49),
|
||||
(2082014, 2040702, 20000, 0, 50),
|
||||
(2082014, 2040705, 25000, 0, 51),
|
||||
(2082014, 2040708, 20000, 0, 52),
|
||||
(2082014, 2040805, 100000, 0, 53),
|
||||
(2082014, 2040816, 100000, 0, 54),
|
||||
(2082014, 2040915, 55000, 0, 55),
|
||||
(2082014, 2040920, 55000, 0, 56),
|
||||
(2082014, 2041008, 38000, 0, 57),
|
||||
(2082014, 2041011, 38000, 0, 58),
|
||||
(2082014, 2041014, 40000, 0, 59),
|
||||
(2082014, 2041017, 40000, 0, 60),
|
||||
(2082014, 2041020, 40000, 0, 61),
|
||||
(2082014, 2041023, 40000, 0, 62),
|
||||
(2082014, 2044902, 50000, 0, 63),
|
||||
(2082014, 2044702, 50000, 0, 64),
|
||||
(2082014, 2043002, 50000, 0, 65),
|
||||
(2082014, 2043802, 50000, 0, 66),
|
||||
(2082014, 2044602, 50000, 0, 67),
|
||||
(2082014, 2049200, 170000, 0, 68),
|
||||
(2082014, 2049201, 220000, 0, 69),
|
||||
(2082014, 2049202, 170000, 0, 70),
|
||||
(2082014, 2049203, 220000, 0, 71),
|
||||
(2082014, 2049204, 170000, 0, 72),
|
||||
(2082014, 2049205, 220000, 0, 73),
|
||||
(2082014, 2049206, 170000, 0, 74),
|
||||
(2082014, 2049207, 220000, 0, 75),
|
||||
(2082014, 2049208, 140000, 0, 76),
|
||||
(2082014, 2049209, 170000, 0, 77),
|
||||
(2082014, 2049210, 140000, 0, 78),
|
||||
(2082014, 2049211, 170000, 0, 79),
|
||||
(2082014, 2040101, 540000, 0, 80),
|
||||
(2082014, 2040100, 700000, 0, 81),
|
||||
(2082014, 2040106, 540000, 0, 82),
|
||||
(2082014, 2040105, 700000, 0, 83),
|
||||
(2082014, 2040201, 540000, 0, 84),
|
||||
(2082014, 2040200, 700000, 0, 85),
|
||||
(2082014, 2040206, 540000, 0, 86),
|
||||
(2082014, 2040205, 700000, 0, 87),
|
||||
(2082014, 2070016, 120000000, 0, 88),
|
||||
(2082014, 2070018, 190000000, 0, 89),
|
||||
(2082014, 2030007, 1800000, 0, 90),
|
||||
(2082014, 4001017, 60000000, 0, 91);
|
||||
|
||||
UPDATE shopitems SET price = 11*price WHERE (`position` >= 33 and `position` <= 79 and `shopid` = 2082014);
|
||||
|
||||
INSERT IGNORE INTO `shopitems` (`shopid`, `itemid`, `price`, `pitch`, `position`) VALUES
|
||||
(1031100, 3010015, 20000, 0, 100),
|
||||
(9110002, 3010019, 7700000, 0, 92),
|
||||
(9110002, 3010008, 10000000, 0, 96),
|
||||
(9110002, 3010007, 10000000, 0, 100),
|
||||
(9201020, 3010009, 4200000, 0, 96),
|
||||
(9201020, 3010014, 7000000, 0, 100),
|
||||
(1081000, 3010013, 4000000, 0, 100),
|
||||
(9201101, 2022338, 2100000, 0, 100),
|
||||
(9201101, 2022339, 2800000, 0, 104),
|
||||
(9201101, 2022340, 4000000, 0, 108),
|
||||
(9201101, 2022341, 2800000, 0, 112),
|
||||
(9201101, 2022342, 2000000, 0, 116),
|
||||
(9201101, 2022343, 4700000, 0, 120),
|
||||
(9201101, 2022344, 5000000, 0, 124),
|
||||
(9201101, 2022345, 4000000, 0, 128),
|
||||
(9201101, 2002028, 5000000, 0, 132),
|
||||
(9201101, 2022544, 100000, 0, 136),
|
||||
(9201101, 2022545, 4400000, 0, 140),
|
||||
(9201101, 2020027, 2100000, 0, 144),
|
||||
(9201101, 2022113, 1800000, 0, 148),
|
||||
(9201101, 2022121, 12000000, 0, 152),
|
||||
(9201101, 2022123, 12000000, 0, 156),
|
||||
(9201101, 2022277, 5300000, 0, 160),
|
||||
(9201101, 2012008, 4200000, 0, 164),
|
||||
(9201101, 2022251, 3800000, 0, 168);
|
||||
|
||||
# adding antibanish scrolls
|
||||
INSERT IGNORE INTO `shopitems` (`shopid`, `itemid`, `price`, `pitch`, `position`) VALUES
|
||||
(1001100, 2030100, 450, 0, 130),
|
||||
(1011100, 2030100, 450, 0, 142),
|
||||
(1021100, 2030100, 450, 0, 142),
|
||||
(1031100, 2030100, 450, 0, 146),
|
||||
(1051002, 2030100, 450, 0, 142),
|
||||
(1052116, 2030100, 450, 0, 118),
|
||||
(1061001, 2030100, 450, 0, 126),
|
||||
(1061002, 2030100, 450, 0, 130),
|
||||
(1091002, 2030100, 450, 0, 130),
|
||||
(1100002, 2030100, 450, 0, 138),
|
||||
(2012005, 2030100, 450, 0, 126),
|
||||
(2022001, 2030100, 450, 0, 126),
|
||||
(2030009, 2030100, 450, 0, 126),
|
||||
(2040051, 2030100, 450, 0, 102),
|
||||
(2041006, 2030100, 450, 0, 134),
|
||||
(2051000, 2030100, 450, 0, 134),
|
||||
(2060004, 2030100, 450, 0, 134),
|
||||
(2070001, 2030100, 450, 0, 134),
|
||||
(2080001, 2030100, 450, 0, 134),
|
||||
(2090003, 2030100, 450, 0, 126),
|
||||
(2093002, 2030100, 450, 0, 126),
|
||||
(2100004, 2030100, 450, 0, 130),
|
||||
(2110001, 2030100, 450, 0, 130),
|
||||
(2130000, 2030100, 450, 0, 126),
|
||||
(9201060, 2030100, 450, 0, 114),
|
||||
(9270021, 2030100, 450, 0, 118),
|
||||
(9270022, 2030100, 450, 0, 114), -- Thanks Rednor for finding duplicate item on NPC
|
||||
(1338, 2030100, 450, 0, 114),
|
||||
(9270057, 2030100, 450, 0, 4),
|
||||
(9270065, 2030100, 450, 0, 3);
|
||||
|
||||
-- Thanks to Vcoc
|
||||
-- GMShop: Sacks, GmEquip, Cheese & Onyx, Utils,
|
||||
-- Arrows, Bullets, Throwings and Capsules,
|
||||
-- Others, Equips, Mounts, Scrolls.
|
||||
|
||||
DELETE FROM `shopitems` WHERE `shopid`=1337;
|
||||
INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `position`) VALUES
|
||||
(1337, 2100036, 1, 1),
|
||||
(1337, 2100035, 1, 2),
|
||||
(1337, 2100034, 1, 3),
|
||||
(1337, 2100033, 1, 4),
|
||||
(1337, 2100007, 1, 5),
|
||||
(1337, 2100006, 1, 6),
|
||||
(1337, 2100005, 1, 7),
|
||||
(1337, 2100004, 1, 8),
|
||||
(1337, 2100003, 1, 9),
|
||||
(1337, 2100002, 1, 10),
|
||||
(1337, 2100001, 1, 11),
|
||||
(1337, 1002959, 1, 12),
|
||||
(1337, 1002140, 1, 13),
|
||||
(1337, 1042003, 1, 14),
|
||||
(1337, 1062007, 1, 15),
|
||||
(1337, 1322013, 1, 16),
|
||||
(1337, 1072010, 1, 17),
|
||||
(1337, 2022179, 1, 18),
|
||||
(1337, 2022273, 1, 19),
|
||||
(1337, 2041200, 1, 20),
|
||||
(1337, 4006001, 1, 21),
|
||||
(1337, 4001017, 1, 22),
|
||||
(1337, 4031179, 1, 23),
|
||||
(1337, 2070018, 1, 24),
|
||||
(1337, 2060004, 1, 25),
|
||||
(1337, 2061004, 1, 26),
|
||||
(1337, 2330005, 1, 27),
|
||||
(1337, 2332000, 1, 28),
|
||||
(1337, 2331000, 1, 29),
|
||||
(1337, 5072000, 1, 30),
|
||||
(1337, 5390000, 1, 31),
|
||||
(1337, 5390001, 1, 32),
|
||||
(1337, 5390002, 1, 33),
|
||||
(1337, 5390005, 1, 34),
|
||||
(1337, 5390006, 1, 35),
|
||||
(1337, 1492013, 1, 36),
|
||||
(1337, 1482013, 1, 37),
|
||||
(1337, 1452044, 1, 38),
|
||||
(1337, 1472052, 1, 39),
|
||||
(1337, 1462039, 1, 40),
|
||||
(1337, 1332050, 1, 41),
|
||||
(1337, 1312031, 1, 42),
|
||||
(1337, 1322052, 1, 43),
|
||||
(1337, 1302059, 1, 44),
|
||||
(1337, 1442045, 1, 45),
|
||||
(1337, 1432038, 1, 46),
|
||||
(1337, 1382036, 1, 47),
|
||||
(1337, 1412026, 1, 48),
|
||||
(1337, 1422028, 1, 49),
|
||||
(1337, 1402036, 1, 50),
|
||||
(1337, 1372032, 1, 51),
|
||||
(1337, 1122000, 1, 52),
|
||||
(1337, 1082149, 1, 53),
|
||||
(1337, 1912000, 1, 54),
|
||||
(1337, 1902000, 1, 55),
|
||||
(1337, 1902001, 1, 56),
|
||||
(1337, 1902002, 1, 57),
|
||||
(1337, 1912005, 1, 58),
|
||||
(1337, 1902005, 1, 59),
|
||||
(1337, 1902006, 1, 60),
|
||||
(1337, 1902007, 1, 61),
|
||||
(1337, 1912011, 1, 62),
|
||||
(1337, 1902015, 1, 63),
|
||||
(1337, 1902016, 1, 64),
|
||||
(1337, 1902017, 1, 65),
|
||||
(1337, 1902018, 1, 66),
|
||||
(1337, 2044908, 1, 67),
|
||||
(1337, 2044815, 1, 68),
|
||||
(1337, 2044512, 1, 69),
|
||||
(1337, 2044712, 1, 70),
|
||||
(1337, 2044612, 1, 71),
|
||||
(1337, 2043312, 1, 72),
|
||||
(1337, 2043117, 1, 73),
|
||||
(1337, 2043217, 1, 74),
|
||||
(1337, 2043023, 1, 75),
|
||||
(1337, 2044417, 1, 76),
|
||||
(1337, 2044317, 1, 77),
|
||||
(1337, 2043812, 1, 78),
|
||||
(1337, 2044117, 1, 79),
|
||||
(1337, 2044217, 1, 80),
|
||||
(1337, 2044025, 1, 81),
|
||||
(1337, 2043712, 1, 82),
|
||||
(1337, 2340000, 1, 83),
|
||||
(1337, 2040807, 1, 84),
|
||||
(1337, 2210032, 1, 85),
|
||||
(1337, 2050004, 1, 86);
|
||||
|
||||
-- Thanks to DietStory v1.02 dev team
|
||||
INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `pitch`, `position`) VALUES
|
||||
(1200001, 3010001, 1000, 0, 1),
|
||||
(1200001, 1092003, 2000, 0, 2),
|
||||
(1200001, 1072063, 10000, 0, 3),
|
||||
(1200001, 1072062, 10000, 0, 4),
|
||||
(1200001, 1072017, 10000, 0, 5),
|
||||
(1200001, 1072049, 5000, 0, 6),
|
||||
(1200001, 1072048, 5000, 0, 7),
|
||||
(1200001, 1072008, 5000, 0, 8),
|
||||
(1200001, 1072005, 50, 0, 9),
|
||||
(1200001, 1072038, 50, 0, 10),
|
||||
(1200001, 1072037, 50, 0, 11),
|
||||
(1200001, 1072001, 50, 0, 12),
|
||||
(1200001, 1062001, 4800, 0, 13),
|
||||
(1200001, 1062000, 4800, 0, 14),
|
||||
(1200001, 1060004, 2800, 0, 15),
|
||||
(1200001, 1060007, 1000, 0, 16),
|
||||
(1200001, 1041012, 3000, 0, 17),
|
||||
(1200001, 1041004, 3000, 0, 18),
|
||||
(1200001, 1040014, 3000, 0, 19),
|
||||
(1200001, 1040013, 3000, 0, 20),
|
||||
(1200001, 1002001, 3000, 0, 21),
|
||||
(1200001, 1002019, 2000, 0, 22),
|
||||
(1200001, 1002134, 800, 0, 23),
|
||||
(1200001, 1002133, 800, 0, 24),
|
||||
(1200001, 1002132, 800, 0, 25),
|
||||
(1200001, 1002069, 450, 0, 26),
|
||||
(1200001, 1002068, 450, 0, 27),
|
||||
(1200001, 1002067, 450, 0, 28),
|
||||
(1200001, 1002066, 450, 0, 29),
|
||||
(1200001, 1002014, 1000, 0, 30),
|
||||
(1200001, 1002008, 500, 0, 31),
|
||||
(1200001, 1332007, 1000, 0, 32),
|
||||
(1200001, 1312000, 3000, 0, 33),
|
||||
(1200001, 1302007, 3000, 0, 34),
|
||||
(1200001, 1322005, 50, 0, 35),
|
||||
(1200001, 1312004, 50, 0, 36),
|
||||
(1200001, 1302000, 50, 0, 37),
|
||||
(1200002, 2330000, 600, 0, 104),
|
||||
(1200002, 2070000, 500, 0, 108),
|
||||
(1200002, 2061000, 1, 0, 120),
|
||||
(1200002, 2060000, 1, 0, 124),
|
||||
(1200002, 2030100, 400, 0, 128),
|
||||
(1200002, 2030000, 400, 0, 132),
|
||||
(1200002, 2020028, 3000, 0, 136),
|
||||
(1200002, 2010004, 310, 0, 140),
|
||||
(1200002, 2010003, 100, 0, 144),
|
||||
(1200002, 2010001, 106, 0, 148),
|
||||
(1200002, 2010002, 50, 0, 152),
|
||||
(1200002, 2010000, 30, 0, 156),
|
||||
(1200002, 2002005, 500, 0, 160),
|
||||
(1200002, 2002004, 500, 0, 164),
|
||||
(1200002, 2002002, 500, 0, 168),
|
||||
(1200002, 2002001, 400, 0, 172),
|
||||
(1200002, 2002000, 500, 0, 176),
|
||||
(1200002, 2000006, 620, 0, 180),
|
||||
(1200002, 2000003, 200, 0, 184),
|
||||
(1200002, 2000002, 320, 0, 188),
|
||||
(1200002, 2000001, 160, 0, 192),
|
||||
(1200002, 2000000, 50, 0, 196),
|
||||
(1301000, 2330000, 600, 0, 104),
|
||||
(1301000, 2070000, 500, 0, 108),
|
||||
(1301000, 2061000, 1, 0, 112),
|
||||
(1301000, 2060000, 1, 0, 116),
|
||||
(1301000, 2030100, 400, 0, 120),
|
||||
(1301000, 2030000, 400, 0, 124),
|
||||
(1301000, 2022000, 1650, 0, 128),
|
||||
(1301000, 2022003, 1100, 0, 132),
|
||||
(1301000, 2002005, 500, 0, 136),
|
||||
(1301000, 2002004, 500, 0, 140),
|
||||
(1301000, 2002002, 500, 0, 144),
|
||||
(1301000, 2002001, 400, 0, 148),
|
||||
(1301000, 2002000, 500, 0, 152),
|
||||
(1301000, 2000006, 620, 0, 156),
|
||||
(1301000, 2000003, 200, 0, 160),
|
||||
(1301000, 2000002, 320, 0, 164),
|
||||
(1301000, 2000015, 160, 0, 168),
|
||||
(1301000, 2000000, 50, 0, 172);
|
||||
|
||||
# adding missing pirate items at Singapore npc's
|
||||
INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `pitch`, `position`) VALUES
|
||||
(9270019, 1492006, 160000, 0, 80),
|
||||
(9270019, 1492005, 100000, 0, 84),
|
||||
(9270019, 1492004, 75000, 0, 88),
|
||||
(9270019, 1482006, 150000, 0, 92),
|
||||
(9270019, 1482005, 100000, 0, 96),
|
||||
(9270019, 1482004, 75000, 0, 100),
|
||||
(9270020, 1052113, 120000, 0, 92),
|
||||
(9270020, 1052110, 100000, 0, 96),
|
||||
(9270020, 1002625, 75000, 0, 100);
|
||||
@@ -1,87 +0,0 @@
|
||||
-- MySQL dump 10.13 Distrib 8.0.22, for Win64 (x86_64)
|
||||
--
|
||||
-- Host: 127.0.0.1 Database: cosmic
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 8.0.19
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!50503 SET NAMES utf8 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
USE `cosmic`;
|
||||
|
||||
--
|
||||
-- Dumping data for table `accounts`
|
||||
--
|
||||
|
||||
LOCK TABLES `accounts` WRITE;
|
||||
/*!40000 ALTER TABLE `accounts` DISABLE KEYS */;
|
||||
INSERT INTO `accounts` VALUES (1,'admin','$2y$12$aFD9BDeUocDMY1X4tDYDyeJw/HhkQwCQWs3KAY7gCaRG0cpqJcaL.','0000','000000',0,'2021-05-24 00:00:01','2021-05-24 00:00:02','2005-05-11',0,NULL,NULL,1000000,1000000,1000000,3,0,'2005-05-11 03:00:00',0,1,NULL,0,NULL,0,NULL,NULL,0,0,'1234-5678',2);
|
||||
/*!40000 ALTER TABLE `accounts` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Dumping data for table `characters`
|
||||
--
|
||||
|
||||
LOCK TABLES `characters` WRITE;
|
||||
/*!40000 ALTER TABLE `characters` DISABLE KEYS */;
|
||||
INSERT INTO `characters` VALUES (1,1,0,'Admin',1,0,0,12,5,4,4,50,5,50,5,0,0,0,0,0,0,0,30030,20000,0,'0,0,0,0,0,0,0,0,0,0',10000,2,6,-1,25,'2021-05-24 00:00:03',1,0,1,0,0,5,0,4,1,0,0,0,0,0,0,0,0,0,0,24,24,24,24,-1,0,5,0,0,0,0,0,0,0,0,0,0,0,'','2021-05-24 00:00:04','2015-01-01 05:00:00',1,0);
|
||||
/*!40000 ALTER TABLE `characters` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Dumping data for table `inventoryequipment`
|
||||
--
|
||||
|
||||
LOCK TABLES `inventoryequipment` WRITE;
|
||||
/*!40000 ALTER TABLE `inventoryequipment` DISABLE KEYS */;
|
||||
INSERT INTO `inventoryequipment` VALUES (17,22,7,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,1,0,-1),(18,23,7,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,0,-1),(19,24,5,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,0,-1),(20,25,7,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,1,0,-1);
|
||||
/*!40000 ALTER TABLE `inventoryequipment` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Dumping data for table `inventoryitems`
|
||||
--
|
||||
|
||||
LOCK TABLES `inventoryitems` WRITE;
|
||||
/*!40000 ALTER TABLE `inventoryitems` DISABLE KEYS */;
|
||||
INSERT INTO `inventoryitems` VALUES (21,1,1,NULL,4161001,4,1,1,'',-1,0,-1,''),(22,1,1,NULL,1040002,-1,-5,1,'',-1,0,-1,''),(23,1,1,NULL,1060002,-1,-6,1,'',-1,0,-1,''),(24,1,1,NULL,1072001,-1,-7,1,'',-1,0,-1,''),(25,1,1,NULL,1302000,-1,-11,1,'',-1,0,-1,'');
|
||||
/*!40000 ALTER TABLE `inventoryitems` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Dumping data for table `keymap`
|
||||
--
|
||||
|
||||
LOCK TABLES `keymap` WRITE;
|
||||
/*!40000 ALTER TABLE `keymap` DISABLE KEYS */;
|
||||
INSERT INTO `keymap` VALUES (161,1,18,4,0),(162,1,65,6,106),(163,1,2,4,10),(164,1,23,4,1),(165,1,3,4,12),(166,1,4,4,13),(167,1,5,4,18),(168,1,6,4,24),(169,1,16,4,8),(170,1,17,4,5),(171,1,19,4,4),(172,1,25,4,19),(173,1,26,4,14),(174,1,27,4,15),(175,1,31,4,2),(176,1,34,4,17),(177,1,35,4,11),(178,1,37,4,3),(179,1,38,4,20),(180,1,40,4,16),(181,1,43,4,9),(182,1,44,5,50),(183,1,45,5,51),(184,1,46,4,6),(185,1,50,4,7),(186,1,56,5,53),(187,1,59,6,100),(188,1,60,6,101),(189,1,61,6,102),(190,1,62,6,103),(191,1,63,6,104),(192,1,64,6,105),(193,1,57,5,54),(194,1,48,4,22),(195,1,29,5,52),(196,1,7,4,21),(197,1,24,4,25),(198,1,33,4,26),(199,1,41,4,23),(200,1,39,4,27);
|
||||
/*!40000 ALTER TABLE `keymap` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Dumping data for table `storages`
|
||||
--
|
||||
|
||||
LOCK TABLES `storages` WRITE;
|
||||
/*!40000 ALTER TABLE `storages` DISABLE KEYS */;
|
||||
INSERT INTO `storages` VALUES (1,1,0,4,0);
|
||||
/*!40000 ALTER TABLE `storages` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
@@ -1,602 +0,0 @@
|
||||
Oblivion Monk Trainee : Dark Katinas (Male)
|
||||
Star Pixie : Star Pixie Starpiece
|
||||
Star Pixie : Blue White-Line Boots
|
||||
Spirit Viking : Scroll for Knuckler for Accuracy 10%
|
||||
Spirit Viking : Spirit Viking Card
|
||||
Spirit Viking : Dark Battle Lord
|
||||
Spirit Viking : Heaven Justice
|
||||
Iron Boar : Maroon Jangoon Boots
|
||||
Iron Boar : Gold Tail
|
||||
Iron Boar : Justice Katara
|
||||
Voodoo : Steel Pow
|
||||
Green Hobi : Pin Hov Charm
|
||||
Green Hobi : Blue Stud
|
||||
Zombie Lupin : Equip Enhancement Scroll
|
||||
Zombie Lupin : Gold Mask
|
||||
Reinforced Mithril Mutae : Reinforced Mithril Mutae Card
|
||||
Yeti : Gold Tail
|
||||
Blue Kentaurus : Kentaurus Marrow
|
||||
Blue Kentaurus : Infinity Wrath
|
||||
Iruvata : Iruvata Card
|
||||
Iruvata : Heaven Justice
|
||||
Snowman : Red-Bean Soup
|
||||
White Fang : White Fang Tail
|
||||
White Fang : Blue Jangoon Boots
|
||||
Green Cornian : Cornian Marrow
|
||||
Green Cornian : [Mastery Book] Venom
|
||||
Green Cornian : Red Katte (Female)
|
||||
Dodo : Whale Helmet
|
||||
Dodo : Scroll for Overall Armor for STR 60%
|
||||
Dodo : [Mastery Book] Slash Storm 20
|
||||
Dodo : Dark Katte (Female)
|
||||
Ultra Gray : Ultra Gray Card
|
||||
Lorang : Lorang Claws
|
||||
Lorang : Dark Piette Pants
|
||||
Buffoon : Buffoon Grandpa Clock
|
||||
Buffoon : Blue Battle Lord
|
||||
Buffoon : Blue Stud
|
||||
Buffoon : Infinity Wrath
|
||||
Flaming Raccoon : Kinoko Ramen (Salt)
|
||||
Red Kentaurus : Kentaurus Flame
|
||||
Red Kentaurus : Scroll for Knuckler for Accuracy 60%
|
||||
Red Kentaurus : Serpent Tongue
|
||||
Red Kentaurus : Dragon Tail
|
||||
Red Kentaurus : Heavenly Katara
|
||||
Mighty Maple Eater : Fireman Axe
|
||||
Tauromacis : Ascalon Katara
|
||||
Tauromacis : Black Pirate Bandana
|
||||
Krappy : Krappi Card
|
||||
Roloduck : Blue Hunter Armor
|
||||
Roloduck : Blue Hunter Pants
|
||||
Crow : Fish Cake (Skewer)
|
||||
Male Mannequin : Male Mannequin Fedora
|
||||
Male Mannequin : Dark Scroll for Accessory for STR 70%
|
||||
Male Mannequin : Male Mannequin Card
|
||||
Male Mannequin : Majestic Katara
|
||||
Leader B : Leader B Charm
|
||||
Leader B : Kinoko Ramen (Roasted Pork)
|
||||
Typhon : Dark Scroll for Cape for Weapon Def 70%
|
||||
Leader A : Leader A Shades
|
||||
Leader A : Dark Scroll for Cape for Magic Def 70%
|
||||
Leader A : Serpent Tongue
|
||||
Soul Teddy : Scroll for Pole Arm for Accuracy 10%
|
||||
Soul Teddy : Steel Pow
|
||||
Soul Teddy : Heavenly Katara
|
||||
Beetle : Beetle Horn
|
||||
Beetle : Equip Enhancement Scroll
|
||||
Beetle : Ascalon Katara
|
||||
Froscola : Blue Battle Lord
|
||||
White Yeti and King Pepe : Scroll for Pole Arm for Accuracy 10%
|
||||
White Yeti and King Pepe : King Pepe Scroll for One-handed BW Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Dagger Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Two-handed Sword Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Two-handed Axe Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Two-handed BW Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Spear Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Polearm Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Wand Magic Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Staff Magic Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Bow Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Crossbow Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Thief Attacks 60%
|
||||
White Yeti and King Pepe : King Pepe Scroll for Knuckle Attacks 60%
|
||||
Harp : Harp Tail Feather
|
||||
Harp : Scroll for Knuckler for Accuracy 100%
|
||||
Harp : Heavenly Katara
|
||||
Harp : Infinity Wrath
|
||||
Lucida : Green Pirate Bottom
|
||||
Lucida : Brown Stud
|
||||
Lucida : Brown Stud Pants
|
||||
Jr. Grupin : Equip Enhancement Scroll
|
||||
Iron Hog : Iron Hog Metal Hoof
|
||||
Sophilia Doll : Fireman Axe
|
||||
Sophilia Doll : Maroon Jangoon Boots
|
||||
Tiru : Tiru Feather
|
||||
Female Mannequin : Female Mannequin Wig
|
||||
Female Mannequin : Dark Scroll for Accessory for STR 70%
|
||||
Female Mannequin : Female Mannequin Card
|
||||
Female Mannequin : Majestic Katara
|
||||
Poison Golem Level 3 : Poison Golem Card
|
||||
Malady : Malady Experimental Frog
|
||||
Malady : Potential Scroll
|
||||
Malady : Steel Pow
|
||||
Vikerola : Dark Battle Lord
|
||||
Wooden Target Dummy : Wooden Target Dummy Card
|
||||
Wooden Target Dummy : Brown Stud
|
||||
Wooden Target Dummy : Brown Stud Pants
|
||||
Cynical Orange Mushroom : Basic Archer Glove
|
||||
Extra A : Littleman A Badge
|
||||
Extra A : Gold Snowboard
|
||||
Extra B : Littleman B Name Plate
|
||||
Extra B : Fairy Honey
|
||||
Extra B : Brown Jangoon Boots
|
||||
Hodori : Majestic Katara
|
||||
Extra C : Littleman C Necklace
|
||||
Extra D : Cat Eye
|
||||
Extra D : Gold Snowboard
|
||||
Extra D : Serpent Tongue
|
||||
King Sage Cat : Scroll for Pole Arm for Accuracy 10%
|
||||
King Sage Cat : Heaven Justice
|
||||
King Sage Cat : Dragon Tail
|
||||
King Sage Cat : Infinity Wrath
|
||||
Homunscullo : Homunsculer Sand
|
||||
Homunscullo : Homunsculer Blood
|
||||
D. Roy : D. Roy Card
|
||||
Nospeed : Arwen Glass Shoes
|
||||
Ribbon Pig : Pig Ribbon
|
||||
Ribbon Pig : Champion Katara
|
||||
Grim Phantom Watch : [Storybook] Black Book
|
||||
Grim Phantom Watch : Grim Phantom Watch Card
|
||||
Qualm Guardian : Blue Katte (Female)
|
||||
Qualm Guardian : Blue Katina Boots
|
||||
Yeti Doll : Justice Katara
|
||||
King Bloctopus : Green White-Line Boots
|
||||
Bigfoot : Bigfoot Toe
|
||||
Green King Goblin : Black Pirate Bandana
|
||||
Dreamy Ghost : Kinoko Ramen (Roasted Pork)
|
||||
MT-09 : MT-09 Fuel
|
||||
Gryphon : Duck Tube [1]
|
||||
Gryphon : Serpent Tongue
|
||||
Risell Squid : Dark Pirate Bottom
|
||||
Dark Axe Stump : Stump Teardrop
|
||||
Blue Wyvern : Equip Enhancement Scroll
|
||||
Nest Golem : Blue Katinas (Male)
|
||||
Cheap Amplifier : Dark Scroll for Accessory for STR 70%
|
||||
Cheap Amplifier : Cheap Amplifier Card
|
||||
Manon : Manon Tail
|
||||
Manon : Manon Cry
|
||||
Saitie : Parwen Entry Pass
|
||||
Saitie : Red Battle Lord
|
||||
Drumming Bunny : Shoes Production Manual
|
||||
Drumming Bunny : Scroll for Pole Arm for Accuracy 10%
|
||||
Drumming Bunny : Gold Snowboard
|
||||
Drumming Bunny : Lion Fang
|
||||
Dyle : Scroll for Knuckler for Accuracy 60%
|
||||
Crimson Tree : Crimson Wood
|
||||
Crimson Tree : Crimson Wood
|
||||
Blue Flower Serpent : Blue Flower Serpent Card
|
||||
Blue Flower Serpent : Scroll for Pole Arm for Accuracy 100%
|
||||
Blue Flower Serpent : Cat Eye
|
||||
Spirit of Rock : Spirit of Rock Music Score
|
||||
Spirit of Rock : Dark Scroll for Accessory for STR 70%
|
||||
Spirit of Rock : Rock Spirit Card
|
||||
Prototype Lord : Prototype Lord Card
|
||||
Seruf : Lion Fang
|
||||
Seruf : Serpent Tongue
|
||||
Dark Wyvern : Blue Katina Boots
|
||||
Mateon : Mateon Tentacle
|
||||
Mateon : Equip Enhancement Scroll
|
||||
Mateon : Dark Pennance
|
||||
Mateon : Gold Pendant
|
||||
Mr. Anchor : [Storybook] Black Book
|
||||
Master Robo : Lion Fang
|
||||
Master Robo : Brown Jangoon Boots
|
||||
Master Robo : Gold Wings
|
||||
Ginseng Jar : Ginseng Jar Card
|
||||
Scuba Pepe : Equip Enhancement Scroll
|
||||
Ghost Pirate : Blue Battle Lord
|
||||
Ghost Pirate : Green Pirate Bottom
|
||||
Ghost Pirate : Heavenly Katara
|
||||
Leatty : Green White-Line Boots
|
||||
Tiv : Tiv Feather
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for One-handed BW Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Dagger Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Two-handed Sword Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Two-handed Axe Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Two-handed BW Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Spear Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Polearm Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Wand Magic Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Staff Magic Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Bow Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Claw Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Crossbow Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Thief Attacks 60%
|
||||
Gold Yeti and King Pepe : King Pepe Scroll for Knuckle Attacks 60%
|
||||
Gold Yeti and King Pepe : Maroon Jangoon Boots
|
||||
Slime : Champion Katara
|
||||
Rodeo : Brown Stud
|
||||
Rodeo : Brown Stud Pants
|
||||
Octopus : Champion Katara
|
||||
Luster Pixie : Luster Pixie Sunpiece
|
||||
Luster Pixie : Luster Pixie Card
|
||||
Nightshadow : Heaven Justice
|
||||
Nightshadow : Infinity Wrath
|
||||
Dual Beetle : Dual Beetle Horn
|
||||
Skeleton Soldier : Skeleton Soldier Card
|
||||
Master Death Teddy : Green Battle Lord
|
||||
Master Death Teddy : Bloodsoaked Katara
|
||||
Sakura Cellion : Maroon Jangoon Boots
|
||||
Pig : Pig Head
|
||||
Fire Boar : Fire Boar Tooth
|
||||
Fire Boar : Arwen Glass Shoes
|
||||
Fire Boar : Fire Boar Card
|
||||
Panda Teddy : Maroon Jangoon Boots
|
||||
Morphed Blin : Serpent Tongue
|
||||
Morphed Blin : Steel Pow
|
||||
Lunar Pixie : Lunar Pixie Moonpiece
|
||||
Lunar Pixie : Gold Wings
|
||||
Curse Eye : "Blackbull" deed to the land
|
||||
Curse Eye : Gold Snowboard
|
||||
Jr. Cellion : Cat Eye
|
||||
Barnard Gray : Barnard Gray Card
|
||||
Faust : Lupin Banana
|
||||
Faust : Lion Fang
|
||||
Faust : Serpent Tongue
|
||||
Gatekeeper : Gate Keeper Card
|
||||
Gatekeeper : Red Battle Lord
|
||||
Griffey : [Mastery Book] Flame Wheel 20
|
||||
Griffey : Meteor Katara
|
||||
Shark : Heaven Justice
|
||||
Deep Buffoon : Deep Buffoon Rock Piece
|
||||
Deep Buffoon : Blue Stud
|
||||
Deep Buffoon : Blazing Dragon Katara
|
||||
Melon Bubble Tea : Melon Bubble Tea Card
|
||||
Melon Bubble Tea : Maroon Jangoon Boots
|
||||
Tino : Tino Feather
|
||||
Bone Fish : Equip Enhancement Scroll
|
||||
Bone Fish : Potential Scroll
|
||||
I.AM.ROBOT : Lion Fang
|
||||
I.AM.ROBOT : Serpent Tongue
|
||||
Lilynouch : Knight Mask
|
||||
Lilynouch : [Mastery Book] Dragon Breath
|
||||
Dark Pepe : Serpent Coil
|
||||
Dark Pepe : Serpent Coil
|
||||
Toy Trojan : Toy Soldier Sword
|
||||
Toy Trojan : Justice Katara
|
||||
Sparker : Scroll for Knuckler for Accuracy 100%
|
||||
Sparker : Justice Katara
|
||||
Dark Drake : Dark Drake Horn
|
||||
Electrophant : Serpent Tongue
|
||||
Electrophant : Lion Fang
|
||||
Tick-Tock : Tick-Tock Egg
|
||||
Mecateon : Mecateon Laser Gun
|
||||
Mecateon : Majestic Katara
|
||||
Helmet Pepe : King Pepe Scroll for Polearm Attacks 60%
|
||||
Helmet Pepe : King Pepe Scroll for Wand Magic Attacks 60%
|
||||
Blue Mushroom : Thermal Fabric
|
||||
Blue Mushroom : Purple Jewelry Shoes
|
||||
Ratz : One-Handed Mace Forging Manual
|
||||
Snow Witch : Ice Tears
|
||||
Flyeye : Flyeye Wing
|
||||
Flyeye : Flyeye Card
|
||||
Headless Horseman : Devil Sunrise
|
||||
Royal Guard : Royal Guard Card
|
||||
Royal Guard : Scroll for Knuckler for Accuracy 10%
|
||||
Royal Guard : Heaven Justice
|
||||
Royal Guard : Dark Battle Lord
|
||||
Triple Rumo : Blue Jangoon Boots
|
||||
Triple Rumo : Brown Jangoon Boots
|
||||
Pianus : Miniature Pianus
|
||||
Pianus : Pianus Scream
|
||||
Pianus : Scroll for Overall Armor for STR 60%
|
||||
Pianus : [Mastery Book] Blaze 20
|
||||
Pianus : Blue Katte (Female)
|
||||
Pianus : Blue Katinas (Male)
|
||||
Rombot : Rombot Memory Card
|
||||
Rombot : Serpent Tongue
|
||||
Roid : Potential Scroll
|
||||
Roid : Equip Enhancement Scroll
|
||||
Robby : Robby Electronic Induction Device
|
||||
Robby : Robby Card
|
||||
Robby : Blue Stud
|
||||
Blue Snail : Grey/Brown Training Shirt
|
||||
Blue Snail : Grey/Brown Training Pants
|
||||
Afterlord : Afterlord Card
|
||||
Afterlord : Scroll for Overall Armor for STR 60%
|
||||
Afterlord : Heaven Gate
|
||||
Afterlord : Bloodsoaked Katara
|
||||
Block Golem : Block Golem Card
|
||||
Lyka : Guardian Horn
|
||||
Lyka : Dark Katinas (Male)
|
||||
Jr. Wraith : Equip Enhancement Scroll
|
||||
Jr. Wraith : Potential Scroll
|
||||
Sentinel : Fireman Axe
|
||||
Ice Drake : Blue Battle Lord
|
||||
Ice Drake : Gold Mask
|
||||
Overlord A : Overlord A Card
|
||||
Overlord B : Overlord B Card
|
||||
Latest Hits Compilation : Dark Scroll for Accessory for STR 70%
|
||||
Latest Hits Compilation : Latest Hits Compilation Card
|
||||
Water Goblin : Serpent Coil
|
||||
Water Goblin : Serpent Coil
|
||||
Retz : Retz Card
|
||||
Retz : Cat Eye
|
||||
Retz : Blue Jangoon Boots
|
||||
Hoodoo : Steel Pow
|
||||
Pink Bean : Pink Bean Card
|
||||
Pink Bean : [Mastery Book] Maple Warrior 30
|
||||
Paper Lantern Ghost : Fish Cake (Skewer)
|
||||
Paper Lantern Ghost : Cat Eye
|
||||
Cold Shark : Frozen Shark Fin
|
||||
Cold Eye : Icicles
|
||||
Croco : Potential Scroll
|
||||
Croco : Gold Pendant
|
||||
Croco : Majestic Katara
|
||||
Bubble Fish : Bubble Fish Thoughts
|
||||
Officer Skeleton : Serpent Coil
|
||||
Officer Skeleton : Serpent Coil
|
||||
Murumuru : Murumuru Furball
|
||||
Zeno : Black Pirate Bandana
|
||||
Panda : Ascalon Katara
|
||||
Panda : Black Pirate Bandana
|
||||
Iron Mutae : Iron Mutae Card
|
||||
Snail : Red Mini Skirt
|
||||
Hankie : Hankie Panfluit
|
||||
Hankie : Heaven Justice
|
||||
Silver Slime : Silver Slime Card
|
||||
Silver Slime : Beige Elf Shoes
|
||||
Zeta Gray : Zeta Gray Card
|
||||
Zeta Gray : Dark Piette Pants
|
||||
Zombie Mushroom : The Charm of the Undead
|
||||
Zombie Mushroom : Hunter Bow
|
||||
Zombie Mushroom : Guardian Katara
|
||||
Pepe : Majestic Katara
|
||||
Grupin : Icicles
|
||||
Grupin : Equip Enhancement Scroll
|
||||
Hobi : Hov Shorts
|
||||
Yabber Doo : Beige Elf Shoes
|
||||
Royal Cactus : Dark Hunter Armor
|
||||
Royal Cactus : Dark Hunter Pants
|
||||
Dual Ghost Pirate : Dual Pirate Propeller
|
||||
Dual Ghost Pirate : Infinity Wrath
|
||||
Memory Monk Trainee : [Mastery Book] Venom 30
|
||||
Memory Monk Trainee : Devil Sunrise
|
||||
Coolie Zombie : Zombie Lost Tooth
|
||||
Coolie Zombie : [Storybook] Black Book
|
||||
Coolie Zombie : Brown Stud
|
||||
Coolie Zombie : Brown Stud Pants
|
||||
Blood Harp : Blood Harp Crown
|
||||
Blood Harp : Heavenly Katara
|
||||
Black Kentaurus : Kentaurus Skull
|
||||
Black Kentaurus : Gold Arund
|
||||
Black Kentaurus : Blazing Dragon Katara
|
||||
Rash : Rash Furball
|
||||
Rash : Anasthetic Powder
|
||||
Imperial Guard : Imperial Guard Card
|
||||
The Book Ghost : The Book Ghost Sheet of Paper
|
||||
The Book Ghost : Blue Jangoon Boots
|
||||
Drake : Drake Meat
|
||||
Drake : Drake Blood
|
||||
Oblivion Guardian : [Mastery Book] Blessing of the Onyx 30
|
||||
Zakum3 : [Skill Book] Advanced Combo
|
||||
Zakum3 : [Mastery Book] Smoke Bomb
|
||||
Zakum3 : [Mastery Book] Sanctuary
|
||||
Zakum3 : [Mastery Book] Venom
|
||||
Zakum3 : [Mastery Book] Crossbow Expert
|
||||
Zakum3 : Zakum Helmet (1)
|
||||
Zakum3 : Devil Sunrise
|
||||
Zakum3 : Zakum Tree Branch
|
||||
Jr. Newtie : Equip Enhancement Scroll
|
||||
Jr. Newtie : Devil Sunrise
|
||||
Jr. Newtie : Red Katte (Female)
|
||||
Jr. Pepe : Jr. Pepe Fish
|
||||
Miner Zombie : Zombie Lost Tooth
|
||||
Miner Zombie : Zombie Lost Gold Tooth
|
||||
Miner Zombie : Minor Zombie Card
|
||||
Miner Zombie : Ascalon Katara
|
||||
Aufheben : Aufheben Time Sand
|
||||
Aufheben : Aufheben Card
|
||||
Eye of Time : Scroll for Overall Armor for STR 60%
|
||||
Eye of Time : Scroll for Knuckler for Accuracy 60%
|
||||
Eye of Time : Heaven Gate
|
||||
Psycho Jack : Fireman Axe
|
||||
Psycho Jack : Red Hunter Armor
|
||||
Psycho Jack : Red Hunter Pants
|
||||
Psycho Jack : Red White-Line Boots
|
||||
Death Teddy : Blazing Dragon Katara
|
||||
Bain : Bain Spiky Collar
|
||||
Bain : Gold Arund
|
||||
Dark Nependeath : Equip Enhancement Scroll
|
||||
Dark Nependeath : Dark Piette Pants
|
||||
Timu : Timu Feather
|
||||
Mano : Rainbow Colored Snail Shell
|
||||
Dark Stone Golem : Dark Stone Golem Card
|
||||
Scorpion : Cat Eye Stone
|
||||
King Clang : Lorang Claws
|
||||
King Clang : Clang Claws
|
||||
Nependeath : Nependeath Honey
|
||||
Phantom Watch : Dragon Tail
|
||||
Phantom Watch : Dark Pirate Bottom
|
||||
Reinforced Iron Mutae : Reinforced Iron Mutae Card
|
||||
Master Chronos : Brown Jangoon Boots
|
||||
Crimson Balrog the Kidnapper : [Skill Book] Dragon Breath
|
||||
Lazy Buffy : Heavenly Katara
|
||||
Red Slime : Kinoko Ramen (Salt)
|
||||
Evil Eye : Cat Eye
|
||||
Maverick Type D : Maverick Type D Card
|
||||
Maverick Type D : Green Battle Lord
|
||||
Maverick Type A : Maverick Type A Card
|
||||
Maverick Type A : Infinity Wrath
|
||||
Klock : Heavenly Katara
|
||||
Tiguru : Tiguru Feather
|
||||
Tortie : Lion Fang
|
||||
Shade : Someone Hat
|
||||
Dunas : Dunas Time Sand
|
||||
Dunas : Dunas Card
|
||||
Timer : Scroll for Knuckler for Accuracy 60%
|
||||
Timer : Serpent Coil
|
||||
Timer : Serpent Coil
|
||||
Maverick Type S : Maverick Type S Card
|
||||
Poison Poopa : Poison Poopa Poisonous Spikes
|
||||
Poison Poopa : Blue White-Line Boots
|
||||
Murupia : Murupia Furball
|
||||
Chief Gray : Chief Gray Sign
|
||||
Chief Gray : Chief Gray Card
|
||||
Chief Gray : Dark Pennance
|
||||
Skelosaurus : Meteor Katara
|
||||
Oberon : Oberon Time Sand
|
||||
Oberon : Oberon Card
|
||||
Mr. Alli : Mr. Alli Leather
|
||||
Mr. Alli : Lion Fang
|
||||
Cellion : Maroon Jangoon Boots
|
||||
Qualm Monk Trainee : [Mastery Book] Blessing of the Onyx 20
|
||||
Chronos : Chronos Egg
|
||||
Kiyo : Kiyo Beak
|
||||
Cherry Bubble Tea : Cherry Bubble Tea Card
|
||||
Cherry Bubble Tea : Maroon Jangoon Boots
|
||||
Pink Teddy : Teddy Yellow Ribbon
|
||||
Pink Teddy : White/Purple Gift Box
|
||||
Rexton : [Mastery Book] Spirit Claw
|
||||
Master of Disguise : The Lost Treasure [1]
|
||||
Master of Disguise : The Lost Treasure [2]
|
||||
Master of Disguise : The Lost Treasure [3]
|
||||
Master of Disguise : The Lost Treasure [4]
|
||||
Master of Disguise : The Lost Treasure [5]
|
||||
Neo Huroid : Gold Mask
|
||||
Jr. Pepe Doll : Guardian Katara
|
||||
Dual Birk : Dual Birk Tiny Tail
|
||||
Muru : Muru Furball
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for One-handed BW Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Dagger Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Two-handed Sword Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Two-handed Axe Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Two-handed BW Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Spear Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Polearm Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Bow Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Crossbow Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Wand Magic Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Staff Magic Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Thief Attacks 60%
|
||||
Grey Yeti and King Pepe : King Pepe Scroll for Knuckle Attacks 60%
|
||||
Wild Kargo : Gold Wings
|
||||
Bubbling : Bubbling Large Bubble
|
||||
Bubbling : Blue Jewelry Shoes
|
||||
Bubbling : Champion Katara
|
||||
Papulatus : [Mastery Book] High Mastery 30
|
||||
Papulatus : Red Katte (Female)
|
||||
Papulatus : Red Katinas (Male)
|
||||
Plateon : Plateon Helmet
|
||||
Plateon : Serpent Tongue
|
||||
Nightghost : Ghost Headband
|
||||
Nightghost : Serpent Coil
|
||||
Nightghost : Serpent Coil
|
||||
Mango Bubble Tea : Mango Bubble Tea Card
|
||||
Mango Bubble Tea : Maroon Jangoon Boots
|
||||
Homunculus : Homunculus Card
|
||||
Homunculus : Green Pirate Bottom
|
||||
Homunculus : Brown Stud
|
||||
Homunculus : Brown Stud Pants
|
||||
Orange Mushroom : Hunter Bow
|
||||
Orange Mushroom : Basic Archer Glove
|
||||
Scarf Plead : Worn-Out Muffler
|
||||
Scarf Plead : Green Hunter Armor
|
||||
Scarf Plead : Green Hunter Pants
|
||||
Female Boss : Lady Boss Comb
|
||||
Female Boss : Devil Sunrise
|
||||
Hogul : Hogul Card
|
||||
Memory Guardian : Meteor Katara
|
||||
Poison Mushroom : King Pepe Scroll for Two-handed Sword Attacks 60%
|
||||
Poison Mushroom : King Pepe Scroll for Two-handed Axe Attacks 60%
|
||||
Poison Mushroom : Justice Katara
|
||||
Homun : Gold Pendant
|
||||
Homun : Ascalon Katara
|
||||
Greatest Oldies : Dark Scroll for Accessory for STR 70%
|
||||
Greatest Oldies : Greatest Oldies Card
|
||||
Kid Mannequin : Kid Mannequin Bunny Suit
|
||||
Kid Mannequin : Dark Scroll for Accessory for STR 70%
|
||||
Kid Mannequin : Kid Mannequin Card
|
||||
Kid Mannequin : Justice Katara
|
||||
Dark Rash : Dark Rash Furball
|
||||
Cerebes : Beige Elf Shoes
|
||||
Grizzly : Equip Enhancement Scroll
|
||||
Lioner : Gold Tail
|
||||
Lioner : Majestic Katara
|
||||
Red Wyvern : Equip Enhancement Scroll
|
||||
Gigantic Spirit Viking : Gigantic Spirit Viking Card
|
||||
Gigantic Spirit Viking : Heaven Gate
|
||||
Gigantic Spirit Viking : Red Battle Lord
|
||||
Gigantic Spirit Viking : Beige Elf Shoes
|
||||
Hector : Blue Jangoon Boots
|
||||
Hector : Serpent Coil
|
||||
Hector : Serpent Coil
|
||||
Crimson Balrog : [Storybook] Crimson Balrog Proposal
|
||||
Memory Monk : [Mastery Book] High Mastery 30
|
||||
Lupin : Lupin Banana
|
||||
Tae Roon : Mind And Heart Medicine
|
||||
Birk : Birk Chewed Grass
|
||||
Birk : Dark Battle Lord
|
||||
Birk : Blazing Dragon Katara
|
||||
Clang : Clang Claws
|
||||
Clang : Lion Fang
|
||||
Straw Target Dummy : Straw Target Dummy Card
|
||||
Royal Guard Pepe : King Pepe Scroll for Crossbow Attacks 60%
|
||||
Royal Guard Pepe : King Pepe Scroll for Thief Attacks 60%
|
||||
Royal Guard Pepe : King Pepe Scroll for Knuckle Attacks 60%
|
||||
Blue King Goblin : Blue Stud
|
||||
Nightmare : Blue Jangoon Boots
|
||||
Renegade Spores : King Pepe Scroll for One-handed BW Attacks 60%
|
||||
Renegade Spores : King Pepe Scroll for Dagger Attacks 60%
|
||||
Renegade Spores : Potential Scroll
|
||||
Prime Minister : King Pepe Scroll for One-handed BW Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Dagger Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Two-handed Sword Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Two-handed Axe Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Two-handed BW Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Spear Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Polearm Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Wand Magic Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Staff Magic Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Bow Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Crossbow Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Thief Attacks 60%
|
||||
Prime Minister : King Pepe Scroll for Knuckle Attacks 60%
|
||||
Prime Minister : Lion Fang
|
||||
Prime Minister : Serpent Tongue
|
||||
Prime Minister : Justice Katara
|
||||
Prime Minister : Guardian Katara
|
||||
Ear Plug Plead : Ear Muffs
|
||||
Oly Oly : Somebody Tire
|
||||
Samiho : Samiho Card
|
||||
Charmer : Charmer Flute
|
||||
Murupa : Murupa Furball
|
||||
Murukun : Murukun Furball
|
||||
Bob : Bob Snail Shell
|
||||
Horny Mushroom : Guardian Katara
|
||||
Brown Teddy : Teddy Cotton
|
||||
Brown Teddy : Equip Enhancement Scroll
|
||||
Brown Teddy : Guardian Katara
|
||||
Thanatos : Thanatos' Strap
|
||||
Thanatos : [Storybook] Black Book
|
||||
Thanatos : Dark Battle Lord
|
||||
Mummydog : Mummydog Card
|
||||
Dark Cornian : Devil Sunrise
|
||||
Dark Cornian : Red Katinas (Male)
|
||||
Intoxicated Pig : King Pepe Scroll for Two-handed BW Attacks 60%
|
||||
Intoxicated Pig : King Pepe Scroll for Spear Attacks 60%
|
||||
Red Flower Serpent : Red Flower Serpent Card
|
||||
Red Flower Serpent : Gold Mask
|
||||
Propelly : Lion Fang
|
||||
Propelly : Fireman Axe
|
||||
Propelly : Dark Pennance
|
||||
Platoon Chronos : Equip Enhancement Scroll
|
||||
Platoon Chronos : Serpent Tongue
|
||||
Chirppy : Fireman Axe
|
||||
Chirppy : Red Hunter Armor
|
||||
Chirppy : Red White-Line Boots
|
||||
Planey : Brown Jangoon Boots
|
||||
Zombie Mushmom : Zombie Mushmom Card
|
||||
Dark Leatty : Black Guise
|
||||
Dark Leatty : Green White-Line Boots
|
||||
Ligator : Equip Enhancement Scroll
|
||||
Male Boss : Boss Pomade
|
||||
Cloud Fox : Sushi (Salmon)
|
||||
Cloud Fox : Fish Cake (Skewer)
|
||||
Cloud Fox : Cat Eye
|
||||
Bodyguard B : Bodyguard B Bullet Shell
|
||||
Baby Typhon : Dark Scroll for Cape for Weapon Def 70%
|
||||
Bodyguard A : Bodyguard A Tie Pin
|
||||
Ergoth : Ergoth Jawbone
|
||||
Annoyed Zombie Mushroom : The Charm of the Undead
|
||||
Annoyed Zombie Mushroom : Hunter Bow
|
||||
Cico : Seahorse Horn
|
||||
Robo : Blue Jangoon Boots
|
||||
Wraith : Cat Eye
|
||||
Wraith : Gold Pendant
|
||||
Red Drake : Dark Piette Pants
|
||||
Killa Bee : Cat Eye
|
||||
Skeledog : Skeledog Bone
|
||||
Skeledog : Maroon Jangoon Boots
|
||||
Windraider : Scroll for Gloves for DEX 15%
|
||||
Tutorial Muru : Tutorial Muru Furball
|
||||
@@ -1,6 +0,0 @@
|
||||
/* Manually run this script in MySQL Workbench or some other database client
|
||||
to migrate your old (pre Jan 19th 2022) monsterbook table to the new version */
|
||||
ALTER TABLE cosmic.`monsterbook`
|
||||
CHANGE COLUMN `charid` `charid` INT(11) NOT NULL,
|
||||
ADD PRIMARY KEY (`charid`, `cardid`),
|
||||
ADD CONSTRAINT `FK_monsterbook_1` FOREIGN KEY (`charid`) REFERENCES `characters` (`id`) ON UPDATE CASCADE ON DELETE CASCADE;
|
||||
@@ -1,7 +0,0 @@
|
||||
---- Cosmic MySQL Database ----
|
||||
|
||||
These SQL files must be executed IN ORDER to set up the database:
|
||||
- 1-db_database.sql (Tables and some data)
|
||||
- 2-db_drops.sql (Remaining data: monster drops, reactor drops)
|
||||
- 3-db_shopupdate.sql (Custom shops - optional, requires provided WZs)
|
||||
- 4-db_admin (Basic admin account - optional)
|
||||
@@ -18,15 +18,15 @@ services:
|
||||
- ./scripts:/opt/server/scripts
|
||||
- ./wz:/opt/server/wz
|
||||
environment:
|
||||
DB_HOST: "db"
|
||||
DB_HOST: "db" ## Remember if this is present it will OVERRIDE the host in the config.yaml, if you put here anything other than db, you'll need to change the config.yaml jdbc string to port 3307, and not port 3306
|
||||
|
||||
db:
|
||||
image: mysql:8.0.23
|
||||
image: mysql:8.4.0
|
||||
environment:
|
||||
MYSQL_RANDOM_ROOT_PASSWORD: "true"
|
||||
MYSQL_DATABASE: "cosmic"
|
||||
MYSQL_USER: "cosmic_server"
|
||||
MYSQL_PASSWORD: "snailshell"
|
||||
MYSQL_ROOT_PASSWORD: ""
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
ports:
|
||||
- "3307:3306"
|
||||
volumes:
|
||||
- ./database/docker-db-data:/var/lib/mysql
|
||||
- ./database/sql:/docker-entrypoint-initdb.d
|
||||
|
||||
@@ -302,10 +302,10 @@ Localhost:
|
||||
* Removed caps for MATK, WDEF, MDEF, ACC and AVOID.
|
||||
* Removed "AP excess" popup and "Admin/MWLB" action block, original credits to kevintjuh93.
|
||||
* Removed "You've gained a level!" popup, original credits to PrinceReborn.
|
||||
* Removed "Cannot enter MTS from this map." popup on maps that blocks transitions (such change channel, CS/MTS), rendering the buyback option now available for all maps.
|
||||
* Removed "Cannot enter MTS from this map." popup on maps that blocks transitions (such change channel, CS/MTS).
|
||||
* Removed a check for players wishing to create/join a party being novices under level 10.
|
||||
* Set a new high cap for SPEED.
|
||||
* Removed the AP assign block for novices.
|
||||
* Removed a block that would show up when trying to apply an attack gem on equipments that aren't weapons.
|
||||
|
||||
---------------------------
|
||||
---------------------------
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
5160006 - Sparkling Eyes - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display sparkling eyes.
|
||||
5160007 - Flaming - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display the look of rage.
|
||||
5160008 - Ray - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display beaming eyes.
|
||||
5160009 - Goo Goo - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display the look of “awesome!!!”
|
||||
5160009 - Goo Goo - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display the look of <EFBFBD>awesome!!!<EFBFBD>
|
||||
5160010 - Whoa Whoa - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display the look of being flustered.
|
||||
5160011 - Constant Sigh - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display the thinking look.
|
||||
5160012 - Drool - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character act like falling asleep.
|
||||
@@ -479,10 +479,8 @@
|
||||
5610001 - Vega's Spell(60%) - This winning spell from Vega enables a 90% success rate on a 60% scroll. Please check the scroll description to confirm that Vega's Spell is available for the scroll you choose.
|
||||
5590000 - High-Five Stamp - Can equip items that are 5 levels above your current level.
|
||||
5021026 - Gift Box Throwing Stars - A gift box that can be freely thrown around. Using the #cThrowing Star# will create an orbital effect.\n\nThis item cannot be deleted.
|
||||
5010073 - Miss Popular - Well, lookie here. Someone’s certainly become popular with the guys. Turn this effect on and off by assigning it to a shortcut key from the keyboard settings menu.
|
||||
|
||||
5010074 - Mr. Popular - Well, lookie here. Someone’s certainly become popular with the girls. Turn this effect on and off by assigning it to a shortcut key from the keyboard settings menu.
|
||||
|
||||
5010073 - Miss Popular - Well, lookie here. Someone<EFBFBD>s certainly become popular with the guys. Turn this effect on and off by assigning it to a shortcut key from the keyboard settings menu.
|
||||
5010074 - Mr. Popular - Well, lookie here. Someone<6E>s certainly become popular with the girls. Turn this effect on and off by assigning it to a shortcut key from the keyboard settings menu.
|
||||
5240027 - Golden Drumstick - A drumstick that can be consumed only by #cBaby Tiger#. It recovers hunger and #cincreases Closeness by 100.#
|
||||
5390005 - Cute Tiger Messenger - Shout to everyone in the world your character is on with this megaphone. Now available with your avatar on the top of everyone's screen! Comes with a tiger background for your avatar.
|
||||
5390006 - Roaring Tiger Messenger - Shout to everyone in the world your character is on with this megaphone. Now available with your avatar on the top of everyone's screen! Comes with a screen shaked.
|
||||
|
||||
200
handbook/Commands.txt
Normal file
200
handbook/Commands.txt
Normal file
@@ -0,0 +1,200 @@
|
||||
Common Commands:
|
||||
@help - Show available commands.
|
||||
@commands - Show available commands.
|
||||
@droplimit - Check drop limit of current map.
|
||||
@time - Show current server time.
|
||||
@credits - Show credits. These people made the server possible.
|
||||
@uptime - Show server online time.
|
||||
@gacha - Show gachapon rewards.
|
||||
@dispose - Dispose to fix NPC chat.
|
||||
@changel - Change language settings
|
||||
@equiplv - Show levels of all equipped items.
|
||||
@showrates - Show all world/character rates.
|
||||
@rates - Show your rates.
|
||||
@online - Show all online players.
|
||||
@gm- Send a message to the game masters.
|
||||
@reportbug - Send in a bug report.
|
||||
@points - Show point total.
|
||||
@joinevent - Join active event.
|
||||
@leaveevent-Leave active event.
|
||||
@ranks - Show player rankings.
|
||||
@str - Assign AP into STR.
|
||||
@dex - Assign AP into DEX.
|
||||
@int - Assign AP into INT.
|
||||
@luk - Assign AP into LUK.
|
||||
@enableauth - Enable PIC code by resetting the cooldown.
|
||||
@toggleexp - Toggle enable/disable all exp gain.
|
||||
@mylawn - Claim ownership of the current map.
|
||||
@bosshp - Show HP of bosses on current map.
|
||||
@mobhp - Show HP of mobs on current map.
|
||||
|
||||
|
||||
Donator Commands:
|
||||
@whatdropsfrom - Show what items drop from a mob.
|
||||
@whodrops - Show what drops an item.
|
||||
@buffme - Activate GM buffs on self.
|
||||
@goto - Warp to a predefined map.
|
||||
|
||||
|
||||
JrGM Commands:
|
||||
!recharge - Recharge and refill all USE items.
|
||||
!whereami - Show info about objects on current map.
|
||||
!hide - Hide from players.
|
||||
!unhide - Toggle Hide.
|
||||
!sp - Set available SP.
|
||||
!ap - Set available AP.
|
||||
!empowerme - Activate all useful buffs.
|
||||
!buffmap - Give GM buffs to the whole map.
|
||||
!buff - Activate a buff.
|
||||
!bomb - Bomb a player, dealing damage.
|
||||
!dc - Disconnect a player.
|
||||
!cleardrops - Clear drops by player.
|
||||
!clearslot - Clear all items in an inventory tab.
|
||||
!clearsavelocs - Clear saved locations for a player.
|
||||
!warp - Warp to a map.
|
||||
!warphere - Move a player to your location.
|
||||
!summon - Move a player to your location.
|
||||
!warpto - Warp to a player.
|
||||
!reach - Warp to a player.
|
||||
!follow - Warp to a player.
|
||||
!gmshop - Open the GM shop.
|
||||
!heal - Fully heal your HP/MP.
|
||||
!item - Spawn an item into your inventory.
|
||||
!drop - Spawn an item onto the ground.
|
||||
!level - Set your level.
|
||||
!levelpro - Set your level, one by one.
|
||||
!setslot - Set amount of inventory slots in all tabs.
|
||||
!setstat - Set all primary stats to new value.
|
||||
!maxstat - Max out all character stats.
|
||||
!maxskill - Max out all job skills.
|
||||
!resetskill Set all skill levels to 0.
|
||||
!search - Search String.wz.
|
||||
!jail - Move a player to the jail.
|
||||
!unjail- Free a player from jail.
|
||||
!job - Change job of a player.
|
||||
!unbug - Unbug self.
|
||||
!id - Search in handbook.
|
||||
!gachalist - Show gachapon rewards.
|
||||
!loot - Loots all items that belong to you.
|
||||
!mobskill - Apply a mob skill to all mobs on the map.
|
||||
Args: <mob skill id> <skill level>
|
||||
|
||||
|
||||
GM Commands:
|
||||
!debuff - Put a debuff on all nearby players.
|
||||
!fly - Enable/disable fly feature.
|
||||
!spawn - Spawn mob(s) on your location.
|
||||
!mutemap - Toggle mute players in the map.
|
||||
!checkdmg - Show stats and damage of a player.
|
||||
!inmap - Show all players in the map.
|
||||
!reloadevents - Reload all event data.
|
||||
!reloaddrops - Reload all drop data.
|
||||
!reloadportals - Reload all portal scripts.
|
||||
!reloadmap - Reload the map.
|
||||
!reloadshops - Reload popup shops and NPC shops.
|
||||
!hpmp - Set HP/MP of a player.
|
||||
!maxhpmp - Set base HP/MP of a player.
|
||||
!music - Play a song.
|
||||
!monitor - Toggle monitored packet logging of a character.
|
||||
!monitors - Show all characters being monitored for packet logging.
|
||||
!ignore - Toggle ignore a character from auto-ban alerts.
|
||||
!ignored - Show all characters being ignored in auto-ban alerts.
|
||||
!pos - Show current position and foothold.
|
||||
!togglecoupon - Toggle enable/disable a coupon.
|
||||
!togglewhitechat - Toggle white GM chat.
|
||||
!fame - Set new fame value of a player.
|
||||
!givenx - Give NX to a player.
|
||||
!givevp - Give vote points to a player.
|
||||
!givems - Give mesos to a player.
|
||||
!giverp - Give reward points to a player.
|
||||
!expeds - Show all ongoing boss expeditions.
|
||||
!kill - Kill a player.
|
||||
!seed - Drop all seeds inside Henesys PQ.
|
||||
!maxenergy - Set dojo energy to max value.
|
||||
!killall - Kill all mobs in the map.
|
||||
!notice - Send a blue message to everyone on the server.
|
||||
!rip - Send a RIP notice.
|
||||
!openportal - Open a portal on the map.
|
||||
!closeportal - Close a portal.
|
||||
!pe - Handle synthesized packets from file, and handle them as if sent from a client.
|
||||
!startevent - Start an event on current map.
|
||||
!endevent - Close entry for ongoing event.
|
||||
!startmapevent - Start a "classic" event on current map.
|
||||
!stopmapevent - Stop ongoing "classic" event.
|
||||
!online2 - Show all online players.
|
||||
!ban - Ban a player.
|
||||
!unban - Unban a player.
|
||||
!healmap - Heal all HP/MP of all players in the map.
|
||||
!healperson - Heal all HP/MP of a player.
|
||||
!hurt - Nearly kill a player.
|
||||
!killmap - Kill all players in the map.
|
||||
!night - Set sky background to black.
|
||||
!npc - Spawn an NPC on your location.
|
||||
!face - Change face of a player.
|
||||
!hair - Change hair of a player.
|
||||
!startquest - Start a quest.
|
||||
!completequest - Complete an active quest.
|
||||
!resetquest - Reset a completed quest.
|
||||
!timer - Set timer on a player in current map.
|
||||
!timermap - Set timer on all players in current map.
|
||||
!timerall Set a server wide timer.
|
||||
!warpmap - Warp all characters on current map to a new map.
|
||||
!warparea - Warp all nearby players to a new map.
|
||||
|
||||
|
||||
SuperGM Commands:
|
||||
!servermessage - Set scrolling server message.
|
||||
!proitem - Spawn an item with custom stats.
|
||||
!seteqstat-Set stats of all equips in inventory.
|
||||
!exprate - Set world exp rate.
|
||||
!mesorate - Set world meso rate.
|
||||
!droprate - Set world drop rate.
|
||||
!bossdroprate - Set world boss drop rate.
|
||||
!questrate - Set world quest rate.
|
||||
!travelrate - Set world travel rate.
|
||||
!fishrate - Set fishing rate.
|
||||
!itemvac - Loot all drops on the map.
|
||||
!forcevac-Loot all drops on the map.
|
||||
!zakum - Spawn Zakum on your location.
|
||||
!horntail - Spawn Horntail on your location.
|
||||
!pinkbean - Spawn Pink Bean on your location.
|
||||
!pap - Spawn Papulatus on your location.
|
||||
!pianus - Spawn Pianus (R) on your location.
|
||||
!cake - Spawn Cake boss with specified HP.
|
||||
!playernpc - Spawn a player NPC of an online player.
|
||||
!playernpcremove - Remove a "Iv 200" player NPC.
|
||||
!pnpc - Spawn a permanent NPC on your location.
|
||||
!pnpcremove - Remove a permanent NPC on the map.
|
||||
!pmob - Spawn a permanent mob on your location.
|
||||
!pmobremove - Remove all permanent mobs of the same type on the map.
|
||||
|
||||
|
||||
Developer Commands:
|
||||
!debug - Show a debug message.
|
||||
!set - Store value in an array, for testing.
|
||||
!showpackets - Toggle show received packets in console.
|
||||
!showmovelife - Toggle show move life in console.
|
||||
!showsessions - Show online sessions.
|
||||
!iplist - Show IP of all players.
|
||||
|
||||
|
||||
Admin Commands:
|
||||
!setgmlevel - Set GM level of a player.
|
||||
!warpworld - Warp to a different world.
|
||||
!saveall - Save all characters.
|
||||
!dcall - Disconnect all players (online or logged in).
|
||||
!mapplayers - Show all players on the map.
|
||||
!getacc - Show account name of an online player.
|
||||
!shutdown - Shut down the server.
|
||||
!clearquestcache - Clear all quest cache.
|
||||
!clearquest - Clear cache of a quest.
|
||||
!supplyratecoupon - Set availability of coupons in Cash Shop.
|
||||
!spawnallpnpcs - Spawn player NPCs of all existing players.
|
||||
!eraseallpnpcs - Remove all player NPCs.
|
||||
!addchannel - Add a new channel to a world.
|
||||
!addworld - Add a new world.
|
||||
!removechannel - Remove channel from a world.
|
||||
!removeworld - Remove a world.
|
||||
!devtest - Runs devtest.js. Developer utility - test stuff without restarting the server.
|
||||
|
||||
|
||||
9
handbook/Equip/Skin.txt
Normal file
9
handbook/Equip/Skin.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
0 - Light - (no description)
|
||||
1 - Tanned - (no description)
|
||||
2 - Dark - (no description)
|
||||
3 - Pale - (no description)
|
||||
4 - Blue - (no description)
|
||||
5 - Green - (no description)
|
||||
9 - White - (no description)
|
||||
10 - Pink - (no description)
|
||||
11 - Brown - (no description)
|
||||
@@ -1779,8 +1779,8 @@
|
||||
4032103 - The Lost Treasure - The lost treasure that was stolen by the Master of Disguise who brazenly entered Ereve. Let's take it back to Irena.
|
||||
4032104 - The Lost Treasure - The lost treasure that was stolen by the Master of Disguise who brazenly entered Ereve. Let's take it back to Eckhart.
|
||||
4032105 - The Lost Treasure - The lost treasure that was stolen by the Master of Disguise who brazenly entered Ereve. Let's take it back to Hawkeye.
|
||||
4000209 - Co-ke Slime’s Bell - The bell of Co-ke Slime that has been taken off.
|
||||
4000210 - Co-ke Pig’s Ribbon - A piece of Co-ke Pig’s ribbon.
|
||||
4000209 - Co-ke Slime<EFBFBD>s Bell - The bell of Co-ke Slime that has been taken off.
|
||||
4000210 - Co-ke Pig<EFBFBD>s Ribbon - A piece of Co-ke Pig<EFBFBD>s ribbon.
|
||||
4000211 - Coca-Cola Cube - A cube with a drawing of a Coca-Cola bottle on it.
|
||||
4000212 - CokePLAY Cube - A cube with the CokePLAY symbol.
|
||||
4000213 - Coca-Cola Card - A card with a drawing of Coca-Cola.
|
||||
@@ -1802,10 +1802,10 @@
|
||||
4001151 - Happy Valley - Happy Valley
|
||||
4001152 - Ariant - Ariant
|
||||
4001153 - Magatia - Magatia
|
||||
4031832 - Sophelia’s Portrait - A portrait of Sophelia.
|
||||
4031832 - Sophelia<EFBFBD>s Portrait - A portrait of Sophelia.
|
||||
4031833 - Pumpkin Juice - A juice made with pumpkin. It is very fragrant.
|
||||
4031834 - Perfect Tool - The most perfect tool for making dolls.
|
||||
4031835 - Lyudmilla’s Earring - The earrings that Lyudmilla lost. They shine with brilliance.
|
||||
4031835 - Lyudmilla<EFBFBD>s Earring - The earrings that Lyudmilla lost. They shine with brilliance.
|
||||
4031836 - Score - The score that Lyudmilla asked for. No one knows what kind of music is in it.
|
||||
4031837 - Dumped Doll - A doll that Sophelia used to cherish a long time ago. She threw it away when she got a new doll.
|
||||
4031838 - Piece of Cloth - A small piece of cloth. If you drag and drop it onto the rag doll, the doll will be completed bit by bit.
|
||||
@@ -1893,26 +1893,26 @@
|
||||
4032127 - Proof of Ability - The Proof of Ability that is required to learn a new skill. This proof signifies that you are ready to learn a new skill.
|
||||
4032128 - Proof of Ability - The Proof of Ability that is required to learn a new skill. This proof signifies that you are ready to learn a new skill.
|
||||
4032129 - Proof of Ability - The Proof of Ability that is required to learn a new skill. This proof signifies that you are ready to learn a new skill.
|
||||
4032130 - Pig Doll - An adorable doll that resembles a pig. However, something doesn't seem right…
|
||||
4032131 - Pig Doll - An adorable doll that resembles a pig. However, something doesn't seem right…
|
||||
4032130 - Pig Doll - An adorable doll that resembles a pig. However, something doesn't seem right<EFBFBD>
|
||||
4032131 - Pig Doll - An adorable doll that resembles a pig. However, something doesn't seem right<EFBFBD>
|
||||
4032132 - Roca's Mission Report - A mission report written by Roca, the Agent for Henesys. This compiles a list of events that have taken place recently.
|
||||
4032136 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. The way it smiles just seems a bit off, as if it has hidden agenda.
|
||||
4032137 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. The way it smiles just seems a bit off, as if it has hidden agenda.
|
||||
4032138 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. It's in…tears.
|
||||
4032138 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. It's in<EFBFBD>tears.
|
||||
4032139 - Old Key - An old, rusty key that Mr. Pickall has been looking for.
|
||||
4032140 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. It's in…tears.
|
||||
4032140 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. It's in<EFBFBD>tears.
|
||||
4032141 - Mattias' Mission Report - A mission report written by Roca, the Agent for Kerning City. This compiles a list of events that have taken place recently.
|
||||
4032142 - Clear Tree Sap - A clear tree sap that can only be found at the very top of the skyscraping trees. Accompanied by the freshest green scent.
|
||||
4032143 - Fruit - A fruit picked from plants near Henesys.
|
||||
4032144 - Hersha's Mission Report - A mission report written by Hersha, the Agent for Ellinia. This compiles a list of events that have taken place recently.
|
||||
4032145 - Detector - A detector made to detect any forces of darkness. Take this to the 10 Boogies.
|
||||
4032146 - Wooden Mask Doll - An adorable doll modeled after the Wooden Mask, but something doesn't seem right…
|
||||
4032147 - Rocky Mask Doll - An adorable doll modeled after the Stone Mask, but something doesn't seem right…
|
||||
4032146 - Wooden Mask Doll - An adorable doll modeled after the Wooden Mask, but something doesn't seem right<EFBFBD>
|
||||
4032147 - Rocky Mask Doll - An adorable doll modeled after the Stone Mask, but something doesn't seem right<EFBFBD>
|
||||
4032148 - Detector - A detector created with the sole purpose of detecting the Puppeteer's movements. We put it to good use, so let's return it to Neinheart.
|
||||
4032149 - 10 Boogies' Mission Report - A mission report written by 10 Boogies, the Agents for Perion. This compiles a list of events that have taken place recently.
|
||||
4032178 - Hack Attempt - A record that shows that Chief Grays tried to hack into the system.
|
||||
4032179 - Ereve Investigation Permit - A permitt that allows one to investigate every part of Ereve. When the Red Alert is on, this permit is the only way you can roam freely around Ereve.
|
||||
4032190 - Orange Mushroom Doll - An adorable doll modeled after the Orange Mushroom, but something doesn't seem right…
|
||||
4032190 - Orange Mushroom Doll - An adorable doll modeled after the Orange Mushroom, but something doesn't seem right<EFBFBD>
|
||||
4032196 - Concentrated Formula: Step 1 - A formula concentrated with effective and potent ingredients. The first formula used to raise Mimiana and Mimio
|
||||
4032197 - Concentrated Formula: Step 2 - A formula concentrated with effective and potent ingredients. The second formula used to raise Mimiana and Mimio.
|
||||
4032198 - Concentrated Formula: Step 3 - A formula concentrated with effective and potent ingredients. The third formula used to raise Mimiana and Mimio.
|
||||
@@ -2048,16 +2048,16 @@
|
||||
4032270 - Glistening Sunlight - Glistening sunlight filled with the feeling of spring.
|
||||
4032271 - Pure Small Sprout - A fragrant new sprout filled with the energies of spring. Used as an ingredient for Pure Perfume.
|
||||
4032272 - Stationery of Longing - Pink stationary that's rumored to return a favorable response if used to send a letter to a secret crush.
|
||||
4032273 - Pencil of Courage X 10 - A pencil that's used to write things you don’t have the courage to say.
|
||||
4032273 - Pencil of Courage X 10 - A pencil that's used to write things you don<EFBFBD>t have the courage to say.
|
||||
4032275 - Pure Normal Sprout - A fragrant new sprout filled with the energies of spring. Used as an ingredient for Pure Perfume.
|
||||
4032276 - Pure Sprout - A fragrant new sprout filled with the energies of spring. Used as an ingredient for Pure Perfume.
|
||||
4032277 - Pure Large Sprout - A fragrant new sprout filled with the energies of spring. Used as an ingredient for Pure Perfume.
|
||||
4032278 - Stationery of Deep Longing - Pink stationary that's rumored to return a favorable response if used to send a letter to a secret crush.
|
||||
4032279 - Stationary of Hope and Longing - Pink stationary that's rumored to return a favorable response if used to send a letter to a secret crush.
|
||||
4032280 - Letter of Love and Longing - Pink stationary that's rumored to return a favorable response if used to send a letter to a secret crush.
|
||||
4032281 - Pencil of Courage X 100 - A pencil that's used to write things you don’t have the courage to say.
|
||||
4032282 - Pencil of Courage X 1000 - AA pencil that's used to write things you don’t have the courage to say.
|
||||
4032283 - Pencil of Courage X 10000 - A pencil that's used to write things you don’t have the courage to say.
|
||||
4032281 - Pencil of Courage X 100 - A pencil that's used to write things you don<EFBFBD>t have the courage to say.
|
||||
4032282 - Pencil of Courage X 1000 - AA pencil that's used to write things you don<EFBFBD>t have the courage to say.
|
||||
4032283 - Pencil of Courage X 10000 - A pencil that's used to write things you don<EFBFBD>t have the courage to say.
|
||||
4032284 - Child's Broken Toy - A toy stolen from a child by a monster. It's broken and cannot be used.
|
||||
4032285 - Child's Broken Toy - A toy stolen from a child by a monster. It's broken and cannot be used.
|
||||
4032286 - Child's Broken Toy - A toy stolen from a child by a monster. It's broken and cannot be used.
|
||||
|
||||
2
handbook/Gender.txt
Normal file
2
handbook/Gender.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
0 - Male - (no description)
|
||||
1 - Female - (no description)
|
||||
70
handbook/Job.txt
Normal file
70
handbook/Job.txt
Normal file
@@ -0,0 +1,70 @@
|
||||
0 - Beginner - (no description)
|
||||
100 - Warrior - (no description)
|
||||
110 - Fighter - (no description)
|
||||
111 - Crusader - (no description)
|
||||
112 - Hero - (no description)
|
||||
120 - Page - (no description)
|
||||
121 - White Knight - (no description)
|
||||
122 - Paladin - (no description)
|
||||
130 - Spearman - (no description)
|
||||
131 - Dragon Knight - (no description)
|
||||
132 - Dark Knight - (no description)
|
||||
200 - Magician - (no description)
|
||||
210 - Fp Wizard - (no description)
|
||||
211 - Fp Mage - (no description)
|
||||
212 - Fp Archmage - (no description)
|
||||
220 - Il Wizard - (no description)
|
||||
221 - Il Mage - (no description)
|
||||
222 - Il Archmage - (no description)
|
||||
230 - Cleric - (no description)
|
||||
231 - Priest - (no description)
|
||||
232 - Bishop - (no description)
|
||||
300 - Bowman - (no description)
|
||||
310 - Hunter - (no description)
|
||||
311 - Ranger - (no description)
|
||||
312 - Bow Master - (no description)
|
||||
320 - Crossbowman - (no description)
|
||||
321 - Sniper - (no description)
|
||||
322 - Crossbow Master - (no description)
|
||||
400 - Thief - (no description)
|
||||
410 - Assassin - (no description)
|
||||
411 - Hermit - (no description)
|
||||
412 - Night Lord - (no description)
|
||||
420 - Bandit - (no description)
|
||||
421 - Chief Bandit - (no description)
|
||||
422 - Shadower - (no description)
|
||||
500 - Pirate - (no description)
|
||||
510 - Brawler - (no description)
|
||||
511 - Marauder - (no description)
|
||||
512 - Buccaneer - (no description)
|
||||
520 - Gunslinger - (no description)
|
||||
521 - Outlaw - (no description)
|
||||
522 - Corsair - (no description)
|
||||
800 - Maple Leaf Brigadier - (no description)
|
||||
900 - Gm - (no description)
|
||||
910 - Super Gm - (no description)
|
||||
1000 - Noblesse - (no description)
|
||||
1100 - Dawn Warrior 1 - (no description)
|
||||
1110 - Dawn Warrior 2 - (no description)
|
||||
1111 - Dawn Warrior 3 - (no description)
|
||||
1200 - Blaze Wizard 1 - (no description)
|
||||
1210 - Blaze Wizard 2 - (no description)
|
||||
1211 - Blaze Wizard 3 - (no description)
|
||||
1212 - Blaze Wizard 4 - (no description)
|
||||
1300 - Wind Archer 1 - (no description)
|
||||
1310 - Wind Archer 2 - (no description)
|
||||
1311 - Wind Archer 3 - (no description)
|
||||
1312 - Wind Archer 4 - (no description)
|
||||
1400 - Night Walker 1 - (no description)
|
||||
1410 - Night Walker 2 - (no description)
|
||||
1411 - Night Walker 3 - (no description)
|
||||
1412 - Night Walker 4 - (no description)
|
||||
1500 - Thunder Breaker 1 - (no description)
|
||||
1510 - Thunder Breaker 2 - (no description)
|
||||
1511 - Thunder Breaker 3 - (no description)
|
||||
1512 - Thunder Breaker 4 - (no description)
|
||||
2000 - Legend - (no description)
|
||||
2100 - Aran 1 - (no description)
|
||||
2110 - Aran 2 - (no description)
|
||||
2111 - Aran 3 - (no description)
|
||||
2112 - Aran 4 - (no description)
|
||||
@@ -1565,9 +1565,9 @@ ossyria
|
||||
200090057 - Empress' Road - To Ellinia
|
||||
200090058 - Empress' Road - To Ereve
|
||||
200090059 - Empress' Road - To Ellinia
|
||||
200010303 - Hidden Street - Eliza’s Garden
|
||||
211040102 - Hidden Street - Snow Soul’s Resting Place
|
||||
209000100 - Happy Village - Cliff’s Hut
|
||||
200010303 - Hidden Street - Eliza<EFBFBD>s Garden
|
||||
211040102 - Hidden Street - Snow Soul<EFBFBD>s Resting Place
|
||||
209000100 - Happy Village - Cliff<EFBFBD>s Hut
|
||||
219000000 - Hidden Street - Coke Town
|
||||
219000001 - Hidden Street - Coke Town Sundry Goods Shop
|
||||
219000002 - Hidden Street - House of Pouch
|
||||
@@ -2863,7 +2863,7 @@ etc
|
||||
980027000 - Goldrich's Maze - Maze of Erosion
|
||||
980027100 - Goldrich's Maze - Maze of Erosion
|
||||
980027200 - Goldrich's Maze - Maze of Erosion
|
||||
980027300 - Goldrich's Maze - Beginning of the Maze…
|
||||
980027300 - Goldrich's Maze - Beginning of the Maze<EFBFBD>
|
||||
980027400 - Goldrich's Maze - Monster's Maze
|
||||
980027500 - Goldrich's Maze - Monster's Maze
|
||||
980027600 - Goldrich's Maze - Monster's Maze
|
||||
@@ -2944,7 +2944,7 @@ etc
|
||||
980027001 - Goldrich's Maze - Maze of Erosion
|
||||
980027101 - Goldrich's Maze - Maze of Erosion
|
||||
980027201 - Goldrich's Maze - Maze of Erosion
|
||||
980027301 - Goldrich's Maze - Beginning of the Maze…
|
||||
980027301 - Goldrich's Maze - Beginning of the Maze<EFBFBD>
|
||||
980027401 - Goldrich's Maze - Monster's Maze
|
||||
980027501 - Goldrich's Maze - Monster's Maze
|
||||
980027601 - Goldrich's Maze - Monster's Maze
|
||||
@@ -3025,7 +3025,7 @@ etc
|
||||
980027002 - Goldrich's Maze - Maze of Erosion
|
||||
980027102 - Goldrich's Maze - Maze of Erosion
|
||||
980027202 - Goldrich's Maze - Maze of Erosion
|
||||
980027302 - Goldrich's Maze - Beginning of the Maze…
|
||||
980027302 - Goldrich's Maze - Beginning of the Maze<EFBFBD>
|
||||
980027402 - Goldrich's Maze - Monster's Maze
|
||||
980027502 - Goldrich's Maze - Monster's Maze
|
||||
980027602 - Goldrich's Maze - Monster's Maze
|
||||
@@ -3349,7 +3349,7 @@ etc
|
||||
913040007 - Opening - Cygnus Knights
|
||||
913040008 - Opening - Cygnus Knights
|
||||
913040009 - Opening - Cygnus Knights
|
||||
920020000 - Hidden Street - Eliza’s Garden
|
||||
920020000 - Hidden Street - Eliza<EFBFBD>s Garden
|
||||
922020300 - Hidden Street - Origin of the Clock Tower
|
||||
922220000 - Hidden Street - Gloomy Forest
|
||||
922230000 - Hidden Street - Lunar World
|
||||
|
||||
@@ -36,10 +36,7 @@
|
||||
5000044 - Orange tiger - They are very gentle and obedient in nature, easily becoming friendly with the owner, which in turn speeds up the level-up process.
|
||||
5000045 - Skunk - Calm and quiet by nature, the easy-going, eccentric Skunk has a rather humorous and pompous outlook on life, the finer things are what it enjoys.
|
||||
5000026 - Sun Wu Kong - Sun Wu Kong ran out of Tang Shan Zhang. He is lazy and moody but can be changed with love.
|
||||
5000040 - ??? ?? - ??? ??? ??? ??? ???????. ??? ????? ??? ?????, ??? ? ?? ??? ??? ???? ??? ?? ????. \n#c??: ?? ??, ??? ??, ??? ?? ???&?? ??, ?? ??, ???? ??, MP ????#
|
||||
5000042 - Kino - A happy-go-lucky, orange mushroom that loves shiny stones.
|
||||
5000043 - ???? - ??? ????, ?? ??? ??? ???????. ??? ?? ? ????, ??? ??? ???? ??? ?? ????. \n#c??: ????, ?????#
|
||||
5000046 - ?? - ???? ?? ?? ???? ???? ??? ???????. \n#c??:????, ?????#
|
||||
5000047 - Robo - A special egg that becomes a mythical Robo upon hatching.
|
||||
5000048 - Baby Robo - A Baby Robo hatched out of the special Capsule. At Level 15, the pet can be evolved into a full-fledged Adult Robo with the help of Garnox the NPC using the Rock of Evolution.
|
||||
5000049 - Blue Robo - Mysterious magical powers aligned and sucessfully evolved the Baby Robo into a Blue Robo!
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
3010058 - WorldEnd - You will recover 50 HP every 10 seconds. Perhaps, as you recline, you will find the answer to many of life's questions.
|
||||
3010057 - BloodyRose - You will recover 50 HP every 10 seconds. You will experience the might of a conqueror after recovery.
|
||||
3010060 - Noblesse Chair - A chair makes you feel like you're sitting in the lap of luxury. Also recovers 50 HP every 10 seconds.
|
||||
3010061 - Underneath the Maple Tree… - A white chair created to celebrate Maple Story's 6th Anniversary. Sit on it to restore 35 HP and 10 MP every 10 seconds.
|
||||
3010061 - Underneath the Maple Tree<EFBFBD> - A white chair created to celebrate Maple Story's 6th Anniversary. Sit on it to restore 35 HP and 10 MP every 10 seconds.
|
||||
3010062 - Bamboo Chair - A chair that restores HP every 10 seconds when used. It's very strong since it was made from bamboo grown on Rien.
|
||||
3010063 - Moon and Star Cushion - A pretty cushion shaped like a moon. Recovers 60 HP every 10 seconds.
|
||||
3010064 - Male Desert Rabbit Cushion - 60 HP is restored every 10 seconds if you lean back on this cute Male Desert Rabbit Cushion.
|
||||
|
||||
@@ -385,7 +385,6 @@
|
||||
2030016 - Phyllia's Warp Powder - Warp powder made by fairy Phyllia. Teleports you to Magatia when used inside the Nihal desert region.
|
||||
2030019 - Return Scroll to Nautilus - This scroll enables you to return to the Pirate village, Nautilus. This is a one use item and will disappear after use.
|
||||
2030020 - Return Scroll to New Leaf City - Use this scroll to venture back to New Leaf City whenever you want!
|
||||
2030100 - Return Scroll - Banished Area - Returns you to the map where you were last banished. Requires immediate use and have changed neither maps nor channels.
|
||||
2031000 - Masked Man's Invitation - An invitation from the Masked Man to the Halloween Party at the Haunted Mansion. Double-click to move straight to the mansion.
|
||||
2031001 - Studio Invitation - An invitation to the studio for the event "For Guild Only".
|
||||
2040000 - Scroll for Helmet for DEF - Improves the helmet's weapon def.\nSuccess rate:100%, weapon def. +1
|
||||
|
||||
259
mvnw
vendored
Normal file
259
mvnw
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
set -euf
|
||||
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||
|
||||
# OS specific support.
|
||||
native_path() { printf %s\\n "$1"; }
|
||||
case "$(uname)" in
|
||||
CYGWIN* | MINGW*)
|
||||
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||
native_path() { cygpath --path --windows "$1"; }
|
||||
;;
|
||||
esac
|
||||
|
||||
# set JAVACMD and JAVACCMD
|
||||
set_java_home() {
|
||||
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||
if [ -n "${JAVA_HOME-}" ]; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||
|
||||
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v java
|
||||
)" || :
|
||||
JAVACCMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v javac
|
||||
)" || :
|
||||
|
||||
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# hash string like Java String::hashCode
|
||||
hash_string() {
|
||||
str="${1:-}" h=0
|
||||
while [ -n "$str" ]; do
|
||||
char="${str%"${str#?}"}"
|
||||
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
||||
str="${str#?}"
|
||||
done
|
||||
printf %x\\n $h
|
||||
}
|
||||
|
||||
verbose() { :; }
|
||||
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||
|
||||
die() {
|
||||
printf %s\\n "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
trim() {
|
||||
# MWRAPPER-139:
|
||||
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||
# Needed for removing poorly interpreted newline sequences when running in more
|
||||
# exotic environments such as mingw bash on Windows.
|
||||
printf "%s" "${1}" | tr -d '[:space:]'
|
||||
}
|
||||
|
||||
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||
while IFS="=" read -r key value; do
|
||||
case "${key-}" in
|
||||
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
||||
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
||||
esac
|
||||
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||
|
||||
case "${distributionUrl##*/}" in
|
||||
maven-mvnd-*bin.*)
|
||||
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
||||
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
||||
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
||||
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
||||
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
||||
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
||||
*)
|
||||
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
||||
distributionPlatform=linux-amd64
|
||||
;;
|
||||
esac
|
||||
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||
;;
|
||||
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||
esac
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
distributionUrlNameMain="${distributionUrlName%.*}"
|
||||
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
||||
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
||||
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
||||
|
||||
exec_maven() {
|
||||
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
||||
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
||||
}
|
||||
|
||||
if [ -d "$MAVEN_HOME" ]; then
|
||||
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
exec_maven "$@"
|
||||
fi
|
||||
|
||||
case "${distributionUrl-}" in
|
||||
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
||||
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
||||
esac
|
||||
|
||||
# prepare tmp dir
|
||||
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
||||
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
||||
trap clean HUP INT TERM EXIT
|
||||
else
|
||||
die "cannot create temp dir"
|
||||
fi
|
||||
|
||||
mkdir -p -- "${MAVEN_HOME%/*}"
|
||||
|
||||
# Download and Install Apache Maven
|
||||
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
verbose "Downloading from: $distributionUrl"
|
||||
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
# select .zip or .tar.gz
|
||||
if ! command -v unzip >/dev/null; then
|
||||
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
fi
|
||||
|
||||
# verbose opt
|
||||
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
||||
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
||||
|
||||
# normalize http auth
|
||||
case "${MVNW_PASSWORD:+has-password}" in
|
||||
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
esac
|
||||
|
||||
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
||||
verbose "Found wget ... using wget"
|
||||
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
||||
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
||||
verbose "Found curl ... using curl"
|
||||
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
||||
elif set_java_home; then
|
||||
verbose "Falling back to use Java to download"
|
||||
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
||||
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
cat >"$javaSource" <<-END
|
||||
public class Downloader extends java.net.Authenticator
|
||||
{
|
||||
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
||||
{
|
||||
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
||||
}
|
||||
public static void main( String[] args ) throws Exception
|
||||
{
|
||||
setDefault( new Downloader() );
|
||||
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
||||
}
|
||||
}
|
||||
END
|
||||
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
||||
verbose " - Compiling Downloader.java ..."
|
||||
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
||||
verbose " - Running Downloader.java ..."
|
||||
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
||||
fi
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
if [ -n "${distributionSha256Sum-}" ]; then
|
||||
distributionSha256Result=false
|
||||
if [ "$MVN_CMD" = mvnd.sh ]; then
|
||||
echo "Checksum validation is not supported for maven-mvnd." >&2
|
||||
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
elif command -v sha256sum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
elif command -v shasum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ $distributionSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# unzip and move
|
||||
if command -v unzip >/dev/null; then
|
||||
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||
else
|
||||
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||
fi
|
||||
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
|
||||
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||
|
||||
clean || :
|
||||
exec_maven "$@"
|
||||
149
mvnw.cmd
vendored
Normal file
149
mvnw.cmd
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
<# : batch portion
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||
@SET __MVNW_CMD__=
|
||||
@SET __MVNW_ERROR__=
|
||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||
@SET PSModulePath=
|
||||
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||
)
|
||||
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||
@SET __MVNW_PSMODULEP_SAVE=
|
||||
@SET __MVNW_ARG0_NAME__=
|
||||
@SET MVNW_USERNAME=
|
||||
@SET MVNW_PASSWORD=
|
||||
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
|
||||
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||
@GOTO :EOF
|
||||
: end batch / begin powershell #>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
if ($env:MVNW_VERBOSE -eq "true") {
|
||||
$VerbosePreference = "Continue"
|
||||
}
|
||||
|
||||
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||
if (!$distributionUrl) {
|
||||
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
}
|
||||
|
||||
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||
"maven-mvnd-*" {
|
||||
$USE_MVND = $true
|
||||
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||
$MVN_CMD = "mvnd.cmd"
|
||||
break
|
||||
}
|
||||
default {
|
||||
$USE_MVND = $false
|
||||
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
if ($env:MVNW_REPOURL) {
|
||||
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
|
||||
}
|
||||
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
|
||||
if ($env:MAVEN_USER_HOME) {
|
||||
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
|
||||
}
|
||||
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||
|
||||
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
exit $?
|
||||
}
|
||||
|
||||
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||
}
|
||||
|
||||
# prepare tmp dir
|
||||
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||
trap {
|
||||
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
}
|
||||
|
||||
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||
|
||||
# Download and Install Apache Maven
|
||||
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
Write-Verbose "Downloading from: $distributionUrl"
|
||||
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
$webclient = New-Object System.Net.WebClient
|
||||
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||
}
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||
if ($distributionSha256Sum) {
|
||||
if ($USE_MVND) {
|
||||
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||
}
|
||||
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||
}
|
||||
}
|
||||
|
||||
# unzip and move
|
||||
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||
try {
|
||||
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||
} catch {
|
||||
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||
Write-Error "fail to move MAVEN_HOME"
|
||||
}
|
||||
} finally {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
99
pom.xml
99
pom.xml
@@ -1,39 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>cosmic-maplestory</groupId>
|
||||
<artifactId>Cosmic</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Cosmic</name>
|
||||
<description>Server emulator for Global MapleStory version 83</description>
|
||||
<url>https://github.com/P0nk/Cosmic</url>
|
||||
<inceptionYear>2021</inceptionYear>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Ponk</name>
|
||||
<email>ponkcode@gmail.com</email>
|
||||
<url>https://github.com/P0nk</url>
|
||||
<roles>
|
||||
<role>maintainer</role>
|
||||
<role>developer</role>
|
||||
</roles>
|
||||
<properties>
|
||||
<discord>ponkcode</discord>
|
||||
</properties>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:https://github.com/P0nk/Cosmic.git</connection>
|
||||
<developerConnection>scm:git:https://github.com/P0nk/Cosmic.git</developerConnection>
|
||||
<url>https://github.com/P0nk/Cosmic</url>
|
||||
</scm>
|
||||
<issueManagement>
|
||||
<system>GitHub Issues</system>
|
||||
<url>https://github.com/P0nk/Cosmic/issues</url>
|
||||
</issueManagement>
|
||||
<ciManagement>
|
||||
<system>GitHub Actions</system>
|
||||
<url>https://github.com/P0nk/Cosmic/actions</url>
|
||||
</ciManagement>
|
||||
|
||||
<properties>
|
||||
<!-- Project -->
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>17</java.version>
|
||||
<java.version>21</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<mainClass>net.server.Server</mainClass>
|
||||
|
||||
<!-- Maven plugins -->
|
||||
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version> <!-- For running unit tests -->
|
||||
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version> <!-- Disabled. (for building thin jar) -->
|
||||
<maven-assembly-plugin.version>3.4.2</maven-assembly-plugin.version> <!-- For packaging the executable fat jar -->
|
||||
<maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version> <!-- For running unit tests -->
|
||||
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version> <!-- Disabled. (for building thin jar) -->
|
||||
<maven-assembly-plugin.version>3.7.1
|
||||
</maven-assembly-plugin.version> <!-- For packaging the executable fat jar -->
|
||||
|
||||
<!-- Dependencies -->
|
||||
<slf4j-api.version>1.7.36</slf4j-api.version> <!-- Logging facade -->
|
||||
<log4j.version>2.18.0</log4j.version> <!-- Slf4j implementation -->
|
||||
<graalvm.version>22.2.0</graalvm.version> <!-- ScriptEngine implementation -->
|
||||
<netty.version>4.1.79.Final</netty.version> <!-- Networking -->
|
||||
<yamlbeans.version>1.15</yamlbeans.version> <!-- Config file -->
|
||||
<slf4j-api.version>2.0.17</slf4j-api.version> <!-- Logging facade -->
|
||||
<log4j.version>2.25.0</log4j.version> <!-- Slf4j implementation -->
|
||||
<graalvm-js.version>23.0.4</graalvm-js.version>
|
||||
<graalvm-js-scriptengine.version>24.0.1</graalvm-js-scriptengine.version> <!-- ScriptEngine implementation -->
|
||||
<netty.version>4.2.2.Final</netty.version> <!-- Networking -->
|
||||
<yamlbeans.version>1.17</yamlbeans.version> <!-- Config file -->
|
||||
<jcip-annotations.version>1.0</jcip-annotations.version> <!-- Annotations for concurrency documentation -->
|
||||
<HikariCP.version>5.0.1</HikariCP.version> <!-- Database connection pool -->
|
||||
<mysql-connector-java.version>8.0.30</mysql-connector-java.version> <!-- MySQL JDBC driver -->
|
||||
<junit.version>5.9.0</junit.version> <!-- Unit test -->
|
||||
<mockito.version>4.7.0</mockito.version> <!-- Unit test -->
|
||||
<HikariCP.version>6.3.0</HikariCP.version> <!-- Database connection pool -->
|
||||
<mysql-connector-j.version>9.3.0</mysql-connector-j.version> <!-- MySQL JDBC driver -->
|
||||
<jdbi-version>3.49.5</jdbi-version> <!-- Convenience wrapper around JDBC -->
|
||||
<liquibase-core.version>4.32.0</liquibase-core.version> <!-- Database migrations -->
|
||||
<junit.version>5.13.1</junit.version> <!-- Unit test -->
|
||||
<mockito.version>5.18.0</mockito.version> <!-- Unit test -->
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -55,9 +90,19 @@
|
||||
<version>${HikariCP.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql-connector-java.version}</version>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>${mysql-connector-j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jdbi</groupId>
|
||||
<artifactId>jdbi3-core</artifactId>
|
||||
<version>${jdbi-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.liquibase</groupId>
|
||||
<artifactId>liquibase-core</artifactId>
|
||||
<version>${liquibase-core.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Networking -->
|
||||
@@ -100,7 +145,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<artifactId>log4j-slf4j2-impl</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
@@ -108,12 +153,12 @@
|
||||
<dependency>
|
||||
<groupId>org.graalvm.js</groupId>
|
||||
<artifactId>js</artifactId>
|
||||
<version>${graalvm.version}</version>
|
||||
<version>${graalvm-js.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.graalvm.js</groupId>
|
||||
<artifactId>js-scriptengine</artifactId>
|
||||
<version>${graalvm.version}</version>
|
||||
<version>${graalvm-js-scriptengine.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
@@ -141,6 +186,12 @@
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -197,4 +248,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
3
scripts/devtest.js
Normal file
3
scripts/devtest.js
Normal file
@@ -0,0 +1,3 @@
|
||||
function run(chr) {
|
||||
chr.message("devtest.js")
|
||||
}
|
||||
@@ -36,12 +36,6 @@ function stopEntry() {
|
||||
}
|
||||
|
||||
function takeoff() {
|
||||
const PacketCreator = Java.type('tools.PacketCreator');
|
||||
|
||||
//sound src: https://www.soundjay.com/transportation/metro-door-close-01.mp3
|
||||
KC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle"));
|
||||
NLC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle"));
|
||||
|
||||
em.setProperty("docked", "false");
|
||||
KC_Waiting.warpEveryone(Subway_to_NLC.getId());
|
||||
NLC_Waiting.warpEveryone(Subway_to_KC.getId());
|
||||
@@ -52,10 +46,6 @@ function arrived() {
|
||||
Subway_to_KC.warpEveryone(KC_docked.getId(), 0);
|
||||
Subway_to_NLC.warpEveryone(NLC_docked.getId(), 0);
|
||||
scheduleNew();
|
||||
|
||||
const PacketCreator = Java.type('tools.PacketCreator');
|
||||
KC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle"));
|
||||
NLC_docked.broadcastMessage(PacketCreator.playSound("subway/whistle"));
|
||||
}
|
||||
|
||||
function cancelSchedule() {}
|
||||
|
||||
@@ -31,7 +31,7 @@ function action(mode, type, selection) {
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
cm.sendSimple("Hey, you look like you need a breather. You should be enjoying the life, just like I am. Well, if you have a couple of items, I can trade you for an item you can play minigames with. Now... what can I do for you?#b\r\n#L0#Create a minigame item#l\r\n#L1#Explain to me what the minigames are about#l#k");
|
||||
cm.sendSimple("Hey, you look like you need a breather from all that hunting. You should be enjoying the life, just like I am. Well, if you have a couple of items, I can trade you for an item you can play minigames with. Now... what can I do for you?#b\r\n#L0#Create a minigame item#l\r\n#L1#Explain to me what the minigames are about#l#k");
|
||||
|
||||
} else if (status == 1) {
|
||||
if (selection == 0) {
|
||||
@@ -57,6 +57,7 @@ function action(mode, type, selection) {
|
||||
if (cm.haveItem(4030012, 15)) {
|
||||
cm.gainItem(4030012, -15);
|
||||
cm.gainItem(4080100, 1);
|
||||
cm.dispose();
|
||||
} else {
|
||||
cm.sendNext("You want #bA set of Match Cards#k? Hmm...to make A set of Match Cards, you'll need some #bMonster Cards#k. Monster Card can be obtained by taking out the monsters all around the island. Collect 15 Monster Cards and you can make a set of A set of Match Cards."); //Lmfao a set of A set xD
|
||||
cm.dispose();
|
||||
@@ -81,7 +82,7 @@ function action(mode, type, selection) {
|
||||
if (current == 1 || current == 2) {
|
||||
cm.sendNextPrev("Enter the room, and when you're ready to play, click on #bReady#k.\r\nOnce the visitor clicks on #bReady#k, the room owner can press #bStart#k to begin the game. If an unwanted visitor walks in, and you don't want to play with that person, the room owner has the right to kick the visitor out of the room. There will be a square box with x written on the right of that person. Click on that for a cold goodbye, okay?"); //Oh yeah, because people WALK in Omok Rooms.
|
||||
} else if (current == 3) {
|
||||
if (cm.haveItem(omok1piece[selection], 99) && cm.haveItem(omok2piece[selection], 99) && cm.haveItem(4030009, 1)) {
|
||||
if (cm.haveItem(omok1piece[selection], omokamount) && cm.haveItem(omok2piece[selection], omokamount) && cm.haveItem(4030009, 1)) {
|
||||
cm.gainItem(omok1piece[selection], -omokamount);
|
||||
cm.gainItem(omok2piece[selection], -omokamount);
|
||||
cm.gainItem(4030009, -1);
|
||||
@@ -95,7 +96,7 @@ function action(mode, type, selection) {
|
||||
|
||||
} else if (status == 5) {
|
||||
if (current == 1) {
|
||||
cm.sendNextPrev("When the first fame starts, #bthe room owner goes first#k. Beward that you'll be given a time limit, and you may lose your turn if you don't make your move on time. Normally, 3 x 3 is not allowed, but if there comes a point that it's absolutely necessary to put your piece there or face ending the game, then you can put it there. 3 x 3 is allowed as the last line of defense! Oh, and it won't count if it's #r6 or 7 straight#k. Only 5!");
|
||||
cm.sendNextPrev("When the first game starts, #bthe room owner goes first#k. Beward that you'll be given a time limit, and you may lose your turn if you don't make your move on time. Normally, 3 x 3 is not allowed, but if there comes a point that it's absolutely necessary to put your piece there or face ending the game, then you can put it there. 3 x 3 is allowed as the last line of defense! Oh, and it won't count if it's #r6 or 7 straight#k. Only 5!");
|
||||
} else if (current == 2) {
|
||||
cm.sendNextPrev("Oh, and unlike Omok, when you create the game room for Match Cards, you'll need to set your game on the number of cards you'll use for the game. There are 3 modes avaliable, 3x4, 4x5, and 5x6, which will require 12, 20, and 30 cards respectively. Remember that you won't beable to change it up once the room is open, so if you really wish to change it up, you may have to close the room and open another one.");
|
||||
}
|
||||
@@ -110,12 +111,14 @@ function action(mode, type, selection) {
|
||||
} else if (status == 7) {
|
||||
if (current == 1) {
|
||||
cm.sendPrev("When the next game starts, the loser will go first. Also, no one is allowed to leave in the middle of a game. If you do, you may need to request either a #bforfeit or tie#k. (Of course, if you request a forfeit, you'll lose the game.) And if you click on 'Leave' in the middle of the game and call to leave after the game, you'll leave the room right after the game is over. This will be a much more useful way to leave.");
|
||||
cm.dispose();
|
||||
} else if (current == 2) {
|
||||
cm.sendNextPrev("If you and your opponent have the same number of matched pairs, then whoever had a longer streak of matched pairs will win. If you ever feel the need to go to the bathroom, or take an extended break, you can request a #btie#k. The game will end in a tie if the opponent accepts the request. Tip: this may be a good way to keep your friendships in tact.");
|
||||
}
|
||||
} else if (status == 8) {
|
||||
if (current == 2) {
|
||||
cm.sendPrev("When the next game starts, the loser will go first. Also, no one is allowed to leave in the middle of a game. If you do, you may need to request either a #bforfeit or tie#k. (Of course, if you request a forfeit, you'll lose the game.) And if you click on 'Leave' in the middle of the game and call to leave after the game, you'll leave the room right after the game is over. This will be a much more useful way to leave.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,25 +21,13 @@ var cpqMaxLvl = 50;
|
||||
var cpqMinAmt = 2;
|
||||
var cpqMaxAmt = 6;
|
||||
|
||||
// Ronan's custom ore refiner NPC
|
||||
var refineRocks = true; // enables moon rock, star rock
|
||||
var refineCrystals = true; // enables common crystals
|
||||
var refineSpecials = true; // enables lithium, special crystals
|
||||
var feeMultiplier = 7.0;
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (!YamlConfig.config.server.USE_CPQ) {
|
||||
if (YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
status = 0;
|
||||
action(1, 0, 4);
|
||||
} else {
|
||||
cm.sendOk("The Monster Carnival is currently unavailable.");
|
||||
cm.dispose();
|
||||
}
|
||||
|
||||
cm.sendOk("The Monster Carnival is currently unavailable.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -239,9 +227,6 @@ function action(mode, type, selection) {
|
||||
} else {
|
||||
if (status == 0) {
|
||||
var talk = "What would you like to do? If you have never participate in the Monster Carnival, you will need to know a few things before participating! \r\n#b#L0# Go to the Monster Carnival 1.#l \r\n#L3# Go to the Monster Carnival 2.#l \r\n#L1# Learn about the Monster Carnival.#l\r\n#L2# Trade #t4001129#.#l";
|
||||
if (YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
talk += "\r\n#L4# ... Can I just refine my ores?#l";
|
||||
}
|
||||
cm.sendSimple(talk);
|
||||
} else if (status == 1) {
|
||||
if (selection == 0) {
|
||||
@@ -269,24 +254,6 @@ function action(mode, type, selection) {
|
||||
cm.warp(980030000, 0);
|
||||
cm.dispose();
|
||||
|
||||
} else if (selection == 4) {
|
||||
var selStr = "Very well, instead I offer a steadfast #bore refining#k service for you, taxing #r" + ((feeMultiplier * 100) | 0) + "%#k over the usual fee to synthetize them. What will you do?#b";
|
||||
|
||||
var options = ["Refine mineral ores", "Refine jewel ores"];
|
||||
if (refineCrystals) {
|
||||
options.push("Refine crystal ores");
|
||||
}
|
||||
if (refineRocks) {
|
||||
options.push("Refine plates/jewels");
|
||||
}
|
||||
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
selStr += "\r\n#L" + i + "# " + options[i] + "#l";
|
||||
}
|
||||
|
||||
cm.sendSimple(selStr);
|
||||
|
||||
status = 76;
|
||||
}
|
||||
} else if (status == 2) {
|
||||
select = selection;
|
||||
@@ -445,170 +412,7 @@ function action(mode, type, selection) {
|
||||
} else if (status == 66) {
|
||||
cm.sendNext("Lastly, while in the Monster Carnival, #byou can not use items / recovery potions that you carry around with you. #kMeanwhile, the monsters let these items fall for good. when, and when you #bget them, the item will immediately activate#k. That's why it's important to know when to get these items.");
|
||||
cm.dispose();
|
||||
} else if (status == 77) {
|
||||
var allDone;
|
||||
|
||||
if (selection == 0) {
|
||||
allDone = refineItems(0); // minerals
|
||||
} else if (selection == 1) {
|
||||
allDone = refineItems(1); // jewels
|
||||
} else if (selection == 2 && refineCrystals) {
|
||||
allDone = refineItems(2); // crystals
|
||||
} else if (selection == 2 && !refineCrystals || selection == 3) {
|
||||
allDone = refineRockItems(); // moon/star rock
|
||||
}
|
||||
|
||||
if (allDone) {
|
||||
cm.sendOk("Done. Thanks for showing up~.");
|
||||
} else {
|
||||
cm.sendOk("Done. Be aware some of the items #rcould not be synthetized#k because either you have a lack of space on your ETC inventory or there's not enough mesos to cover the fee.");
|
||||
}
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getRefineFee(fee) {
|
||||
return ((feeMultiplier * fee) | 0);
|
||||
}
|
||||
|
||||
function isRefineTarget(refineType, refineItemid) {
|
||||
if (refineType == 0) { //mineral refine
|
||||
return refineItemid >= 4010000 && refineItemid <= 4010007 && !(refineItemid == 4010007 && !refineSpecials);
|
||||
} else if (refineType == 1) { //jewel refine
|
||||
return refineItemid >= 4020000 && refineItemid <= 4020008 && !(refineItemid == 4020008 && !refineSpecials);
|
||||
} else if (refineType == 2) { //crystal refine
|
||||
return refineItemid >= 4004000 && refineItemid <= 4004004 && !(refineItemid == 4004004 && !refineSpecials);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getRockRefineTarget(refineItemid) {
|
||||
if (refineItemid >= 4011000 && refineItemid <= 4011006) {
|
||||
return 0;
|
||||
} else if (refineItemid >= 4021000 && refineItemid <= 4021008) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
function refineItems(refineType) {
|
||||
var allDone = true;
|
||||
|
||||
var refineFees = [[300, 300, 300, 500, 500, 500, 800, 270], [500, 500, 500, 500, 500, 500, 500, 1000, 3000], [5000, 5000, 5000, 5000, 1000000]];
|
||||
var itemCount = {};
|
||||
|
||||
const InventoryType = Java.type('client.inventory.InventoryType');
|
||||
var iter = cm.getPlayer().getInventory(InventoryType.ETC).iterator();
|
||||
while (iter.hasNext()) {
|
||||
var it = iter.next();
|
||||
var itemid = it.getItemId();
|
||||
|
||||
if (isRefineTarget(refineType, itemid)) {
|
||||
var ic = itemCount[itemid];
|
||||
|
||||
if (ic != undefined) {
|
||||
itemCount[itemid] += it.getQuantity();
|
||||
} else {
|
||||
itemCount[itemid] = it.getQuantity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in itemCount) {
|
||||
var itemqty = itemCount[key];
|
||||
var itemid = parseInt(key);
|
||||
|
||||
var refineQty = ((itemqty / 10) | 0);
|
||||
if (refineQty <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
itemqty = refineQty * 10;
|
||||
|
||||
var fee = getRefineFee(refineFees[refineType][(itemid % 100) | 0] * refineQty);
|
||||
if (cm.canHold(itemid + 1000, refineQty, itemid, itemqty) && cm.getMeso() >= fee) {
|
||||
cm.gainMeso(-fee);
|
||||
cm.gainItem(itemid, -itemqty);
|
||||
cm.gainItem(itemid + (itemid != 4010007 ? 1000 : 1001), refineQty);
|
||||
|
||||
break;
|
||||
} else if (refineQty <= 1) {
|
||||
allDone = false;
|
||||
break;
|
||||
} else {
|
||||
refineQty--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allDone;
|
||||
}
|
||||
|
||||
function refineRockItems() {
|
||||
var allDone = true;
|
||||
var minItems = [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]];
|
||||
var minRocks = [2147483647, 2147483647];
|
||||
|
||||
var rockItems = [4011007, 4021009];
|
||||
var rockFees = [10000, 15000];
|
||||
|
||||
const InventoryType = Java.type('client.inventory.InventoryType');
|
||||
var iter = cm.getPlayer().getInventory(InventoryType.ETC).iterator();
|
||||
while (iter.hasNext()) {
|
||||
var it = iter.next();
|
||||
var itemid = it.getItemId();
|
||||
var rockRefine = getRockRefineTarget(itemid);
|
||||
if (rockRefine >= 0) {
|
||||
var rockItem = ((itemid % 100) | 0);
|
||||
var itemqty = it.getQuantity();
|
||||
|
||||
minItems[rockRefine][rockItem] += itemqty;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < minRocks.length; i++) {
|
||||
for (var j = 0; j < minItems[i].length; j++) {
|
||||
if (minRocks[i] > minItems[i][j]) {
|
||||
minRocks[i] = minItems[i][j];
|
||||
}
|
||||
}
|
||||
if (minRocks[i] <= 0 || minRocks[i] == 2147483647) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var refineQty = minRocks[i];
|
||||
while (true) {
|
||||
var fee = getRefineFee(rockFees[i] * refineQty);
|
||||
if (cm.canHold(rockItems[i], refineQty) && cm.getMeso() >= fee) {
|
||||
cm.gainMeso(-fee);
|
||||
|
||||
var j;
|
||||
if (i == 0) {
|
||||
for (j = 4011000; j < 4011007; j++) {
|
||||
cm.gainItem(j, -refineQty);
|
||||
}
|
||||
cm.gainItem(j, refineQty);
|
||||
} else {
|
||||
for (j = 4021000; j < 4021009; j++) {
|
||||
cm.gainItem(j, -refineQty);
|
||||
}
|
||||
cm.gainItem(j, refineQty);
|
||||
}
|
||||
|
||||
break;
|
||||
} else if (refineQty <= 1) {
|
||||
allDone = false;
|
||||
break;
|
||||
} else {
|
||||
refineQty--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allDone;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
1.0 - First Version by Drago (MapleStorySA)
|
||||
2.0 - Second Version by Ronan (HeavenMS)
|
||||
3.0 - Third Version by Jayd - translated CPQ contents to English and added Pirate items
|
||||
Special thanks to 頼晏 (ryantpayton) for also stepping in to translate CPQ scripts.
|
||||
---------------------------------------------------------------------------------------------------
|
||||
**/
|
||||
|
||||
@@ -20,25 +21,13 @@ var cpqMaxLvl = 50;
|
||||
var cpqMinAmt = 2;
|
||||
var cpqMaxAmt = 6;
|
||||
|
||||
// Ronan's custom ore refiner NPC
|
||||
var refineRocks = true; // enables moon rock, star rock
|
||||
var refineCrystals = true; // enables common crystals
|
||||
var refineSpecials = true; // enables lithium, special crystals
|
||||
var feeMultiplier = 7.0;
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (!YamlConfig.config.server.USE_CPQ) {
|
||||
if (YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
status = 0;
|
||||
action(1, 0, 4);
|
||||
} else {
|
||||
cm.sendOk("The Monster Carnival is currently unavailable.");
|
||||
cm.dispose();
|
||||
}
|
||||
|
||||
cm.sendOk("The Monster Carnival is currently unavailable.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -238,9 +227,6 @@ function action(mode, type, selection) {
|
||||
} else {
|
||||
if (status == 0) {
|
||||
var talk = "What would you like to do? If you have never participate in the Monster Carnival, you will need to know a few things before participating! \r\n#b#L0# Go to the Monster Carnival 1.#l \r\n#L3# Go to the Monster Carnival 2.#l \r\n#L1# Learn about the Monster Carnival.#l\r\n#L2# Trade #t4001129#.#l";
|
||||
if (YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
talk += "\r\n#L4# ... Can I just refine my ores?#l";
|
||||
}
|
||||
cm.sendSimple(talk);
|
||||
} else if (status == 1) {
|
||||
if (selection == 0) {
|
||||
@@ -268,24 +254,6 @@ function action(mode, type, selection) {
|
||||
cm.warp(980030000, 0);
|
||||
cm.dispose();
|
||||
|
||||
} else if (selection == 4) {
|
||||
var selStr = "Very well, instead I offer a steadfast #bore refining#k service for you, taxing #r" + ((feeMultiplier * 100) | 0) + "%#k over the usual fee to synthetize them. What will you do?#b";
|
||||
|
||||
var options = ["Refine mineral ores", "Refine jewel ores"];
|
||||
if (refineCrystals) {
|
||||
options.push("Refine crystal ores");
|
||||
}
|
||||
if (refineRocks) {
|
||||
options.push("Refine plates/jewels");
|
||||
}
|
||||
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
selStr += "\r\n#L" + i + "# " + options[i] + "#l";
|
||||
}
|
||||
|
||||
cm.sendSimple(selStr);
|
||||
|
||||
status = 76;
|
||||
}
|
||||
} else if (status == 2) {
|
||||
select = selection;
|
||||
@@ -439,175 +407,12 @@ function action(mode, type, selection) {
|
||||
cm.sendNext("Oh, and do not worry about turning into a ghost. In the Monster Carnival, #byou will not lose EXP after death#k. So it's really an experience like no other!");
|
||||
cm.dispose();
|
||||
} else if (select == 2) {
|
||||
cm.sendNext("#bProtetor#k basically an invoked item that drastically increases the abilities of the monsters invoked by your group. Protector works until it is demolished by the opposing group, so I'm hoping you'll summon several monsters first, and then bring the Protector.");
|
||||
cm.sendNext("#bProtector#k is basically an invoked item that drastically increases the abilities of the monsters invoked by your group. Protector works until it is demolished by the opposing group, so I'm hoping you'll summon several monsters first, and then bring the Protector.");
|
||||
}
|
||||
} else if (status == 66) {
|
||||
cm.sendNext("Lastly, while in the Monster Carnival, #byou can not use items / recovery potions that you carry around with you. #kMeanwhile, the monsters let these items fall for good. when, and when you #bget them, the item will immediately activate#k. That's why it's important to know when to get these items.");
|
||||
cm.dispose();
|
||||
} else if (status == 77) {
|
||||
var allDone;
|
||||
|
||||
if (selection == 0) {
|
||||
allDone = refineItems(0); // minerals
|
||||
} else if (selection == 1) {
|
||||
allDone = refineItems(1); // jewels
|
||||
} else if (selection == 2 && refineCrystals) {
|
||||
allDone = refineItems(2); // crystals
|
||||
} else if (selection == 2 && !refineCrystals || selection == 3) {
|
||||
allDone = refineRockItems(); // moon/star rock
|
||||
}
|
||||
|
||||
if (allDone) {
|
||||
cm.sendOk("Done. Thanks for showing up~.");
|
||||
} else {
|
||||
cm.sendOk("Done. Be aware some of the items #rcould not be synthetized#k because either you have a lack of space on your ETC inventory or there's not enough mesos to cover the fee.");
|
||||
}
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getRefineFee(fee) {
|
||||
return ((feeMultiplier * fee) | 0);
|
||||
}
|
||||
|
||||
function isRefineTarget(refineType, refineItemid) {
|
||||
if (refineType == 0) { //mineral refine
|
||||
return refineItemid >= 4010000 && refineItemid <= 4010007 && !(refineItemid == 4010007 && !refineSpecials);
|
||||
} else if (refineType == 1) { //jewel refine
|
||||
return refineItemid >= 4020000 && refineItemid <= 4020008 && !(refineItemid == 4020008 && !refineSpecials);
|
||||
} else if (refineType == 2) { //crystal refine
|
||||
return refineItemid >= 4004000 && refineItemid <= 4004004 && !(refineItemid == 4004004 && !refineSpecials);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getRockRefineTarget(refineItemid) {
|
||||
if (refineItemid >= 4011000 && refineItemid <= 4011006) {
|
||||
return 0;
|
||||
} else if (refineItemid >= 4021000 && refineItemid <= 4021008) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
function refineItems(refineType) {
|
||||
var allDone = true;
|
||||
|
||||
var refineFees = [[300, 300, 300, 500, 500, 500, 800, 270], [500, 500, 500, 500, 500, 500, 500, 1000, 3000], [5000, 5000, 5000, 5000, 1000000]];
|
||||
var itemCount = {};
|
||||
|
||||
const InventoryType = Java.type('client.inventory.InventoryType');
|
||||
var iter = cm.getPlayer().getInventory(InventoryType.ETC).iterator();
|
||||
while (iter.hasNext()) {
|
||||
var it = iter.next();
|
||||
var itemid = it.getItemId();
|
||||
|
||||
if (isRefineTarget(refineType, itemid)) {
|
||||
var ic = itemCount[itemid];
|
||||
|
||||
if (ic != undefined) {
|
||||
itemCount[itemid] += it.getQuantity();
|
||||
} else {
|
||||
itemCount[itemid] = it.getQuantity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in itemCount) {
|
||||
var itemqty = itemCount[key];
|
||||
var itemid = parseInt(key);
|
||||
|
||||
var refineQty = ((itemqty / 10) | 0);
|
||||
if (refineQty <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
itemqty = refineQty * 10;
|
||||
|
||||
var fee = getRefineFee(refineFees[refineType][(itemid % 100) | 0] * refineQty);
|
||||
if (cm.canHold(itemid + 1000, refineQty, itemid, itemqty) && cm.getMeso() >= fee) {
|
||||
cm.gainMeso(-fee);
|
||||
cm.gainItem(itemid, -itemqty);
|
||||
cm.gainItem(itemid + (itemid != 4010007 ? 1000 : 1001), refineQty);
|
||||
|
||||
break;
|
||||
} else if (refineQty <= 1) {
|
||||
allDone = false;
|
||||
break;
|
||||
} else {
|
||||
refineQty--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allDone;
|
||||
}
|
||||
|
||||
function refineRockItems() {
|
||||
var allDone = true;
|
||||
var minItems = [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]];
|
||||
var minRocks = [2147483647, 2147483647];
|
||||
|
||||
var rockItems = [4011007, 4021009];
|
||||
var rockFees = [10000, 15000];
|
||||
|
||||
const InventoryType = Java.type('client.inventory.InventoryType');
|
||||
var iter = cm.getPlayer().getInventory(InventoryType.ETC).iterator();
|
||||
while (iter.hasNext()) {
|
||||
var it = iter.next();
|
||||
var itemid = it.getItemId();
|
||||
var rockRefine = getRockRefineTarget(itemid);
|
||||
if (rockRefine >= 0) {
|
||||
var rockItem = ((itemid % 100) | 0);
|
||||
var itemqty = it.getQuantity();
|
||||
|
||||
minItems[rockRefine][rockItem] += itemqty;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < minRocks.length; i++) {
|
||||
for (var j = 0; j < minItems[i].length; j++) {
|
||||
if (minRocks[i] > minItems[i][j]) {
|
||||
minRocks[i] = minItems[i][j];
|
||||
}
|
||||
}
|
||||
if (minRocks[i] <= 0 || minRocks[i] == 2147483647) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var refineQty = minRocks[i];
|
||||
while (true) {
|
||||
var fee = getRefineFee(rockFees[i] * refineQty);
|
||||
if (cm.canHold(rockItems[i], refineQty) && cm.getMeso() >= fee) {
|
||||
cm.gainMeso(-fee);
|
||||
|
||||
var j;
|
||||
if (i == 0) {
|
||||
for (j = 4011000; j < 4011007; j++) {
|
||||
cm.gainItem(j, -refineQty);
|
||||
}
|
||||
cm.gainItem(j, refineQty);
|
||||
} else {
|
||||
for (j = 4021000; j < 4021009; j++) {
|
||||
cm.gainItem(j, -refineQty);
|
||||
}
|
||||
cm.gainItem(j, refineQty);
|
||||
}
|
||||
|
||||
break;
|
||||
} else if (refineQty <= 1) {
|
||||
allDone = false;
|
||||
break;
|
||||
} else {
|
||||
refineQty--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allDone;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
1.0 - First Version by Drago (MapleStorySA)
|
||||
2.0 - Second Version by Ronan (HeavenMS)
|
||||
3.0 - Third Version by Jayd - translated CPQ contents to English and added Pirate items
|
||||
Special thanks to 頼晏 (ryantpayton) for also stepping in to translate CPQ scripts.
|
||||
---------------------------------------------------------------------------------------------------
|
||||
**/
|
||||
|
||||
@@ -20,25 +21,13 @@ var cpqMaxLvl = 50;
|
||||
var cpqMinAmt = 2;
|
||||
var cpqMaxAmt = 6;
|
||||
|
||||
// Ronan's custom ore refiner NPC
|
||||
var refineRocks = true; // enables moon rock, star rock
|
||||
var refineCrystals = true; // enables common crystals
|
||||
var refineSpecials = true; // enables lithium, special crystals
|
||||
var feeMultiplier = 7.0;
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (!YamlConfig.config.server.USE_CPQ) {
|
||||
if (YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
status = 0;
|
||||
action(1, 0, 4);
|
||||
} else {
|
||||
cm.sendOk("The Monster Carnival is currently unavailable.");
|
||||
cm.dispose();
|
||||
}
|
||||
|
||||
cm.sendOk("The Monster Carnival is currently unavailable.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -59,6 +48,7 @@ function action(mode, type, selection) {
|
||||
status--;
|
||||
}
|
||||
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (cm.getPlayer().getMapId() == 980000010) {
|
||||
if (status == 0) {
|
||||
cm.sendNext("I hope you had fun at the Monster Carnival!");
|
||||
@@ -222,9 +212,9 @@ function action(mode, type, selection) {
|
||||
}
|
||||
} else {
|
||||
var party = cm.getParty().getMembers();
|
||||
if ((selection >= 0 && selection <= 3) && party.size() < 1) {
|
||||
if ((selection >= 0 && selection <= 3) && party.size() < (YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 2)) {
|
||||
cm.sendOk("You need at least 2 players to participate in the battle!");
|
||||
} else if ((selection >= 4 && selection <= 5) && party.size() < 1) {
|
||||
} else if ((selection >= 4 && selection <= 5) && party.size() < (YamlConfig.config.server.USE_ENABLE_SOLO_EXPEDITIONS ? 1 : 3)) {
|
||||
cm.sendOk("You need at least 3 players to participate in the battle!");
|
||||
} else {
|
||||
cm.cpqLobby(selection);
|
||||
@@ -237,11 +227,6 @@ function action(mode, type, selection) {
|
||||
} else {
|
||||
if (status == 0) {
|
||||
var talk = "What would you like to do? If you have never participate in the Monster Carnival, you will need to know a few things before participating! \r\n#b#L0# Go to the Monster Carnival 1.#l \r\n#L3# Go to the Monster Carnival 2.#l \r\n#L1# Learn about the Monster Carnival.#l\r\n#L2# Trade #t4001129#.#l";
|
||||
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
talk += "\r\n#L4# ... Can I just refine my ores?#l";
|
||||
}
|
||||
cm.sendSimple(talk);
|
||||
} else if (status == 1) {
|
||||
if (selection == 0) {
|
||||
@@ -269,24 +254,6 @@ function action(mode, type, selection) {
|
||||
cm.warp(980030000, 0);
|
||||
cm.dispose();
|
||||
|
||||
} else if (selection == 4) {
|
||||
var selStr = "Very well, instead I offer a steadfast #bore refining#k service for you, taxing #r" + ((feeMultiplier * 100) | 0) + "%#k over the usual fee to synthetize them. What will you do?#b";
|
||||
|
||||
var options = ["Refine mineral ores", "Refine jewel ores"];
|
||||
if (refineCrystals) {
|
||||
options.push("Refine crystal ores");
|
||||
}
|
||||
if (refineRocks) {
|
||||
options.push("Refine plates/jewels");
|
||||
}
|
||||
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
selStr += "\r\n#L" + i + "# " + options[i] + "#l";
|
||||
}
|
||||
|
||||
cm.sendSimple(selStr);
|
||||
|
||||
status = 76;
|
||||
}
|
||||
} else if (status == 2) {
|
||||
select = selection;
|
||||
@@ -310,7 +277,7 @@ function action(mode, type, selection) {
|
||||
}
|
||||
} else if (select == 2) {//S2 Warrior 26 S3 Magician 6 S4 Bowman 6 S5 Thief 8
|
||||
status = 10;
|
||||
cm.sendSimple("Please make sure you have #t4001129# for the weapon you want. Select the weapon you would like to trade #t4001129#. The choices I have are really good, and I'm not the one who speaks to the people who say it! \r\n#b#L0# #z1302004# (" + n3 + " coins)#l\r\n#L1# #z1402006# (" + n3 + " coins)#l\r\n#L2# #z1302009# (" + n4 + " coins)#l\r\n#L3# #z1402007# (" + n4 + " coins)#l\r\n#L4# #z1302010# (" + n5 + " coins)#l\r\n#L5# #z1402003# (" + n5 + " coins)#l\r\n#L6# #z1312006# (" + n3 + " coins)#l\r\n#L7# #z1412004# (" + n3 + " coins)#l\r\n#L8# #z1312007# (" + n4 + " coins)#l\r\n#L9# #z1412005# (" + n4 + " coins)#l\r\n#L10# #z1312008# (" + n5 + " coins)#l\r\n#L11# #z1412003# (" + n5 + " coins)#l\r\n#L12# Continue to the next page(1/2)#l");
|
||||
cm.sendSimple("Please make sure you have # t4001129 # for the weapon you want. Select the weapon you would like to trade # t4001129 #. The choices I have are really good, and I'm not the one who speaks to the people who say it! \r\n#b#L0# #z1302004# (" + n3 + " coins)#l\r\n#L1# #z1402006# (" + n3 + " coins)#l\r\n#L2# #z1302009# (" + n4 + " coins)#l\r\n#L3# #z1402007# (" + n4 + " coins)#l\r\n#L4# #z1302010# (" + n5 + " coins)#l\r\n#L5# #z1402003# (" + n5 + " coins)#l\r\n#L6# #z1312006# (" + n3 + " coins)#l\r\n#L7# #z1412004# (" + n3 + " coins)#l\r\n#L8# #z1312007# (" + n4 + " coins)#l\r\n#L9# #z1412005# (" + n4 + " coins)#l\r\n#L10# #z1312008# (" + n5 + " coins)#l\r\n#L11# #z1412003# (" + n5 + " coins)#l\r\n#L12# Continue to the next page (1/2)#l");
|
||||
} else if (select == 3) {
|
||||
status = 20;
|
||||
cm.sendSimple("Select the weapon you would like to trade. The weapons I have here are extremely attractive. See for yourself! \r\n#b#L0# #z1372001# (" + n3 + " coins)#l\r\n#L1# #z1382018# (" + n3 + " coins)#l\r\n#L2# #z1372012# (" + n4 + " coins)#l\r\n#L3# #z1382019# (" + n4 + " coins)#l\r\n#L4# #z1382001# (" + n5 + " coins)#l\r\n#L5# #z1372007# (" + n5 + " coins)#l");
|
||||
@@ -440,175 +407,12 @@ function action(mode, type, selection) {
|
||||
cm.sendNext("Oh, and do not worry about turning into a ghost. In the Monster Carnival, #byou will not lose EXP after death#k. So it's really an experience like no other!");
|
||||
cm.dispose();
|
||||
} else if (select == 2) {
|
||||
cm.sendNext("#bProtetor#k basically an invoked item that drastically increases the abilities of the monsters invoked by your group. Protector works until it is demolished by the opposing group, so I'm hoping you'll summon several monsters first, and then bring the Protector.");
|
||||
cm.sendNext("#bProtector#k is basically an invoked item that drastically increases the abilities of the monsters invoked by your group. Protector works until it is demolished by the opposing group, so I'm hoping you'll summon several monsters first, and then bring the Protector.");
|
||||
}
|
||||
} else if (status == 66) {
|
||||
cm.sendNext("Lastly, while in the Monster Carnival, #byou can not use items / recovery potions that you carry around with you. #kMeanwhile, the monsters let these items fall for good. when, and when you #bget them, the item will immediately activate#k. That's why it's important to know when to get these items.");
|
||||
cm.dispose();
|
||||
} else if (status == 77) {
|
||||
var allDone;
|
||||
|
||||
if (selection == 0) {
|
||||
allDone = refineItems(0); // minerals
|
||||
} else if (selection == 1) {
|
||||
allDone = refineItems(1); // jewels
|
||||
} else if (selection == 2 && refineCrystals) {
|
||||
allDone = refineItems(2); // crystals
|
||||
} else if (selection == 2 && !refineCrystals || selection == 3) {
|
||||
allDone = refineRockItems(); // moon/star rock
|
||||
}
|
||||
|
||||
if (allDone) {
|
||||
cm.sendOk("Done. Thanks for showing up~.");
|
||||
} else {
|
||||
cm.sendOk("Done. Be aware some of the items #rcould not be synthetized#k because either you have a lack of space on your ETC inventory or there's not enough mesos to cover the fee.");
|
||||
}
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getRefineFee(fee) {
|
||||
return ((feeMultiplier * fee) | 0);
|
||||
}
|
||||
|
||||
function isRefineTarget(refineType, refineItemid) {
|
||||
if (refineType == 0) { //mineral refine
|
||||
return refineItemid >= 4010000 && refineItemid <= 4010007 && !(refineItemid == 4010007 && !refineSpecials);
|
||||
} else if (refineType == 1) { //jewel refine
|
||||
return refineItemid >= 4020000 && refineItemid <= 4020008 && !(refineItemid == 4020008 && !refineSpecials);
|
||||
} else if (refineType == 2) { //crystal refine
|
||||
return refineItemid >= 4004000 && refineItemid <= 4004004 && !(refineItemid == 4004004 && !refineSpecials);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getRockRefineTarget(refineItemid) {
|
||||
if (refineItemid >= 4011000 && refineItemid <= 4011006) {
|
||||
return 0;
|
||||
} else if (refineItemid >= 4021000 && refineItemid <= 4021008) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
function refineItems(refineType) {
|
||||
var allDone = true;
|
||||
|
||||
var refineFees = [[300, 300, 300, 500, 500, 500, 800, 270], [500, 500, 500, 500, 500, 500, 500, 1000, 3000], [5000, 5000, 5000, 5000, 1000000]];
|
||||
var itemCount = {};
|
||||
|
||||
const InventoryType = Java.type('client.inventory.InventoryType');
|
||||
var iter = cm.getPlayer().getInventory(InventoryType.ETC).iterator();
|
||||
while (iter.hasNext()) {
|
||||
var it = iter.next();
|
||||
var itemid = it.getItemId();
|
||||
|
||||
if (isRefineTarget(refineType, itemid)) {
|
||||
var ic = itemCount[itemid];
|
||||
|
||||
if (ic != undefined) {
|
||||
itemCount[itemid] += it.getQuantity();
|
||||
} else {
|
||||
itemCount[itemid] = it.getQuantity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in itemCount) {
|
||||
var itemqty = itemCount[key];
|
||||
var itemid = parseInt(key);
|
||||
|
||||
var refineQty = ((itemqty / 10) | 0);
|
||||
if (refineQty <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
itemqty = refineQty * 10;
|
||||
|
||||
var fee = getRefineFee(refineFees[refineType][(itemid % 100) | 0] * refineQty);
|
||||
if (cm.canHold(itemid + 1000, refineQty, itemid, itemqty) && cm.getMeso() >= fee) {
|
||||
cm.gainMeso(-fee);
|
||||
cm.gainItem(itemid, -itemqty);
|
||||
cm.gainItem(itemid + (itemid != 4010007 ? 1000 : 1001), refineQty);
|
||||
|
||||
break;
|
||||
} else if (refineQty <= 1) {
|
||||
allDone = false;
|
||||
break;
|
||||
} else {
|
||||
refineQty--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allDone;
|
||||
}
|
||||
|
||||
function refineRockItems() {
|
||||
var allDone = true;
|
||||
var minItems = [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]];
|
||||
var minRocks = [2147483647, 2147483647];
|
||||
|
||||
var rockItems = [4011007, 4021009];
|
||||
var rockFees = [10000, 15000];
|
||||
|
||||
const InventoryType = Java.type('client.inventory.InventoryType');
|
||||
var iter = cm.getPlayer().getInventory(InventoryType.ETC).iterator();
|
||||
while (iter.hasNext()) {
|
||||
var it = iter.next();
|
||||
var itemid = it.getItemId();
|
||||
var rockRefine = getRockRefineTarget(itemid);
|
||||
if (rockRefine >= 0) {
|
||||
var rockItem = ((itemid % 100) | 0);
|
||||
var itemqty = it.getQuantity();
|
||||
|
||||
minItems[rockRefine][rockItem] += itemqty;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < minRocks.length; i++) {
|
||||
for (var j = 0; j < minItems[i].length; j++) {
|
||||
if (minRocks[i] > minItems[i][j]) {
|
||||
minRocks[i] = minItems[i][j];
|
||||
}
|
||||
}
|
||||
if (minRocks[i] <= 0 || minRocks[i] == 2147483647) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var refineQty = minRocks[i];
|
||||
while (true) {
|
||||
var fee = getRefineFee(rockFees[i] * refineQty);
|
||||
if (cm.canHold(rockItems[i], refineQty) && cm.getMeso() >= fee) {
|
||||
cm.gainMeso(-fee);
|
||||
|
||||
var j;
|
||||
if (i == 0) {
|
||||
for (j = 4011000; j < 4011007; j++) {
|
||||
cm.gainItem(j, -refineQty);
|
||||
}
|
||||
cm.gainItem(j, refineQty);
|
||||
} else {
|
||||
for (j = 4021000; j < 4021009; j++) {
|
||||
cm.gainItem(j, -refineQty);
|
||||
}
|
||||
cm.gainItem(j, refineQty);
|
||||
}
|
||||
|
||||
break;
|
||||
} else if (refineQty <= 1) {
|
||||
allDone = false;
|
||||
break;
|
||||
} else {
|
||||
refineQty--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allDone;
|
||||
}
|
||||
}
|
||||
@@ -40,10 +40,7 @@ function action(mode, type, selection) {
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
cm.openShopNPC(2082014);
|
||||
} else if (cm.isQuestStarted(3749)) {
|
||||
if (cm.isQuestStarted(3749)) {
|
||||
cm.sendOk("We've already located the enemy's ultimate weapon! Follow along the ship's bow area ahead and you will find my sister #b#p2082013##k. Report to her for futher instructions on the mission.");
|
||||
} else {
|
||||
cm.sendDefault();
|
||||
|
||||
@@ -23,7 +23,12 @@ function action(mode, type, selection) {
|
||||
}
|
||||
|
||||
if (!cm.isEventLeader()) {
|
||||
cm.sendYesNo("I wish for your leader to talk to me. Alternatively, you may be wanting to quit. Are you going to abandon this campaign?");
|
||||
// Player chose "No" or "End Chat"
|
||||
if (mode <= 0) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
cm.sendYesNo("I wish for your leader to talk to me. Alternatively, you may be wanting to quit. Are you going to abandon this campaign?");
|
||||
}
|
||||
} else {
|
||||
var eim = cm.getEventInstance();
|
||||
if (eim == null) {
|
||||
|
||||
@@ -58,8 +58,8 @@ function action(mode, type, selection) {
|
||||
}
|
||||
}
|
||||
if (cm.getChar().getGender() == 1) {
|
||||
for (var i = 0; i < fface.length; i++) {
|
||||
pushIfItemExists(facenew, fface[i] + cm.getChar().getFace()
|
||||
for (var i = 0; i < fface_v.length; i++) {
|
||||
pushIfItemExists(facenew, fface_v[i] + cm.getChar().getFace()
|
||||
% 1000 - (cm.getChar().getFace()
|
||||
% 100));
|
||||
}
|
||||
|
||||
@@ -32,14 +32,14 @@ function action(mode, type, selection) {
|
||||
} else if (status == 1) {
|
||||
if (selection == 0) {
|
||||
apqpoints = cm.getPlayer().getAriantPoints();
|
||||
if (apqpoints < 100) {
|
||||
cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points. You need to surpass #b100 points#k so that I can give you the #bPalm Tree Beach Chair#k. Talk to me again when you have enough points.");
|
||||
cm.dispose();
|
||||
if (apqpoints >= 100) {
|
||||
cm.sendNext("Wow, it looks like you got the #b100#k points ready to trade, let's trade?!");
|
||||
} else if (apqpoints + arena.getAriantRewardTier(cm.getPlayer()) >= 100) {
|
||||
cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points and you pratically already have that score! Talk to my wife, #p2101016#to get them and then re-chat with me!");
|
||||
cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points and you pratically already have that score! Talk to my wife, #p2101016# to get them and then re-chat with me!");
|
||||
cm.dispose();
|
||||
} else {
|
||||
cm.sendNext("Wow, it looks like you got the #b100#k points ready to trade, let's trade?!");
|
||||
cm.sendOk("Your Battle Arena score: #b" + apqpoints + "#k points. You need to surpass #b100 points#k so that I can give you the #bPalm Tree Beach Chair#k. Talk to me again when you have enough points.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (selection == 1) {
|
||||
cm.sendOk("The main objective of the Battle Arena is to allow the player to accumulate points so that they can be traded honorably for the highest prize: the #bPalm Tree Beach Chair#k. Collect points during the battles and talk to me when it's time to get the prize. In each battle, the player is given the opportunity to score points based on the amount of jewelry that the player has at the end. But be careful! If your points distance from other players #ris too high#k, this will have been all for nothing and you will earn mere #r1 point#k only.");
|
||||
|
||||
@@ -17,127 +17,9 @@
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* Coco
|
||||
Refining NPC:
|
||||
* Chaos scroll SYNTHETIZER (rofl)
|
||||
*
|
||||
* @author RonanLana (ronancpl)
|
||||
*/
|
||||
|
||||
var status = 0;
|
||||
var selectedType = -1;
|
||||
var selectedItem = -1;
|
||||
var item;
|
||||
var mats;
|
||||
var matQty;
|
||||
var cost;
|
||||
var qty;
|
||||
var equip;
|
||||
var last_use; //last item is a use item
|
||||
/* Coco */
|
||||
|
||||
function start() {
|
||||
cm.getPlayer().setCS(true);
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == 1) {
|
||||
status++;
|
||||
} else {
|
||||
cm.sendOk("Oh, ok... Talk back to us when you want to make business.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (!YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
cm.sendOk("Hi, I'm #b#p" + cm.getNpc() + "##k.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
var selStr = "Hey traveler! Come, come closer... We offer a #bhuge opportunity of business#k to you. If you want to know what it is, keep listening...";
|
||||
cm.sendNext(selStr);
|
||||
} else if (status == 1) {
|
||||
var selStr = "We've got here the knowledge to synthetize the mighty #b#t2049100##k! Of course, making one is not an easy task... But worry not! Just gather some material to me and a fee of #b1,200,000 mesos#k for our services to #bobtain it#k. You still want to do it?";
|
||||
cm.sendYesNo(selStr);
|
||||
} else if (status == 2) {
|
||||
//selectedItem = selection;
|
||||
selectedItem = 0;
|
||||
|
||||
var itemSet = [2049100, 7777777];
|
||||
var matSet = new Array([4031203, 4001356, 4000136, 4000082, 4001126, 4080100, 4000021, 4003005]);
|
||||
var matQtySet = new Array([100, 60, 40, 80, 10, 8, 200, 120]);
|
||||
var costSet = [1200000, 7777777];
|
||||
item = itemSet[selectedItem];
|
||||
mats = matSet[selectedItem];
|
||||
matQty = matQtySet[selectedItem];
|
||||
cost = costSet[selectedItem];
|
||||
|
||||
var prompt = "So, you want us to make some #t" + item + "#? In that case, how many do you want us to make?";
|
||||
cm.sendGetNumber(prompt, 1, 1, 100)
|
||||
} else if (status == 3) {
|
||||
qty = (selection > 0) ? selection : (selection < 0 ? -selection : 1);
|
||||
last_use = false;
|
||||
|
||||
var prompt = "You want us to make ";
|
||||
if (qty == 1) {
|
||||
prompt += "a #t" + item + "#?";
|
||||
} else {
|
||||
prompt += qty + " #t" + item + "#?";
|
||||
}
|
||||
|
||||
prompt += " In that case, we're going to need specific items from you in order to make it. Make sure you have room in your inventory, though!#b";
|
||||
|
||||
if (mats instanceof Array) {
|
||||
for (var i = 0; i < mats.length; i++) {
|
||||
prompt += "\r\n#i" + mats[i] + "# " + matQty[i] * qty + " #t" + mats[i] + "#";
|
||||
}
|
||||
} else {
|
||||
prompt += "\r\n#i" + mats + "# " + matQty * qty + " #t" + mats + "#";
|
||||
}
|
||||
|
||||
if (cost > 0) {
|
||||
prompt += "\r\n#i4031138# " + cost * qty + " meso";
|
||||
}
|
||||
cm.sendYesNo(prompt);
|
||||
} else if (status == 4) {
|
||||
var complete = true;
|
||||
|
||||
if (cm.getMeso() < cost * qty) {
|
||||
cm.sendOk("Come on! We're not here doing you a favor! We all need money to live properly, so bring the cash so we make deal and start the synthesis.");
|
||||
} else if (!cm.canHold(item, qty)) {
|
||||
cm.sendOk("You didn't check if you got a slot to spare on your inventory before our business, no?");
|
||||
} else {
|
||||
if (mats instanceof Array) {
|
||||
for (var i = 0; complete && i < mats.length; i++) {
|
||||
if (matQty[i] * qty == 1) {
|
||||
complete = cm.haveItem(mats[i]);
|
||||
} else {
|
||||
complete = cm.haveItem(mats[i], matQty[i] * qty);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
complete = cm.haveItem(mats, matQty * qty);
|
||||
}
|
||||
|
||||
if (!complete) {
|
||||
cm.sendOk("You kidding, right? We won't be able to start the process without all the ingredients at hands. Go get all of them and then talk to us!");
|
||||
} else {
|
||||
if (mats instanceof Array) {
|
||||
for (var i = 0; i < mats.length; i++) {
|
||||
cm.gainItem(mats[i], -matQty[i] * qty);
|
||||
}
|
||||
} else {
|
||||
cm.gainItem(mats, -matQty * qty);
|
||||
}
|
||||
cm.gainMeso(-cost * qty);
|
||||
cm.gainItem(item, qty);
|
||||
cm.sendOk("Wow... can't believe it worked! To think for a moment that it could f... Ahem. Of course it worked, all work of ours are very efficient! Nice doing business with you.");
|
||||
}
|
||||
}
|
||||
cm.dispose();
|
||||
}
|
||||
cm.sendDefault();
|
||||
cm.dispose();
|
||||
}
|
||||
@@ -19,208 +19,9 @@
|
||||
*/
|
||||
/* NPC: Agent E (9000036)
|
||||
Victoria Road : Henesys
|
||||
|
||||
Refining NPC:
|
||||
* Accessories refiner
|
||||
*
|
||||
* @author Ronan Lana
|
||||
*/
|
||||
|
||||
var status = -1;
|
||||
var selectedType = -1;
|
||||
var selectedItem = -1;
|
||||
var item;
|
||||
var items;
|
||||
var mats;
|
||||
var matQty;
|
||||
var cost;
|
||||
var qty = 1;
|
||||
var equip;
|
||||
var maxEqp = 0;
|
||||
|
||||
function start() {
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (!YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
cm.sendOk("Hi, I'm #b#p" + cm.getNpc() + "##k.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
cm.getPlayer().setCS(true);
|
||||
var selStr = "Hello, I am the #bAccessory NPC Crafter#k! My works are widely recognized to be too fine, up to the point at which all my items mimic not only the appearance but too the attributes of them! Everything I charge is some 'ingredients' to make them and, of course, a fee for my services. On what kind of equipment are you interessed?#b";
|
||||
var options = ["Pendants", "Face accessories", "Eye accessories", "Belts & medals", "Rings"/*,"#t4032496#"*/];
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
selStr += "\r\n#L" + i + "# " + options[i] + "#l";
|
||||
}
|
||||
cm.sendSimple(selStr);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
status++;
|
||||
if (mode != 1) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (status == 0) {
|
||||
if (selection == 0) { //pendants
|
||||
var selStr = "Well, I've got these pendants on my repertoire:#b";
|
||||
items = [1122018, 1122007, 1122001, 1122003, 1122004, 1122006, 1122002, 1122005, 1122058];
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
selStr += "\r\n#L" + i + "##t" + items[i] + "##b";
|
||||
}
|
||||
} else if (selection == 1) { //face accessory
|
||||
var selStr = "Hmm, face accessories? There you go: #b";
|
||||
items = [1012181, 1012182, 1012183, 1012184, 1012185, 1012186, 1012108, 1012109, 1012110, 1012111];
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
selStr += "\r\n#L" + i + "##t" + items[i] + "##b";
|
||||
}
|
||||
} else if (selection == 2) { //eye accessory
|
||||
var selStr = "Got hard sight? Okay, so which glasses do you want me to make?#b";
|
||||
items = [1022073, 1022088, 1022103, 1022089, 1022082];
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
selStr += "\r\n#L" + i + "##t" + items[i] + "##b";
|
||||
}
|
||||
} else if (selection == 3) { //belt & medal
|
||||
var selStr = "Hmm... For these, things get a little tricky. Since these items are too short and too similar one another, I don't really know what item will emerge when I finish the synthesis. Still wanna try for something?";
|
||||
items = [];
|
||||
maxEqp = 0;
|
||||
|
||||
for (var x = 1132005; x < 1132017; maxEqp++, x++) {
|
||||
items[maxEqp] = x;
|
||||
}
|
||||
|
||||
for (var x = 1142000; x < 1142102; maxEqp++, x++) {
|
||||
items[maxEqp] = x;
|
||||
}
|
||||
|
||||
for (var x = 1142107; x < 1142121; maxEqp++, x++) {
|
||||
items[maxEqp] = x;
|
||||
}
|
||||
|
||||
for (var x = 1142122; x < 1142143; maxEqp++, x++) {
|
||||
items[maxEqp] = x;
|
||||
}
|
||||
selStr += "\r\n#L" + i + "##bTry it!#b";
|
||||
|
||||
} else if (selection == 4) { //ring refine
|
||||
var selStr = "Rings, huh? These are my specialty, go check it yourself!#b";
|
||||
items = [1112407, 1112408, 1112401, 1112413, 1112414, 1112405, 1112402];
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
selStr += "\r\n#L" + i + "##t" + items[i] + "##b";
|
||||
}
|
||||
|
||||
}/*else if (selection == 5) { //make necklace
|
||||
var selStr = "Need to make #t4032496#?#b";
|
||||
items = [4032496];
|
||||
for (var i = 0; i < items.length; i++)
|
||||
selStr += "\r\n#L" + i + "##t" + items[i] + "##l";
|
||||
}*/
|
||||
selectedType = selection;
|
||||
cm.sendSimple(selStr);
|
||||
} else if (status == 1) {
|
||||
if (selectedType != 3) {
|
||||
selectedItem = selection;
|
||||
}
|
||||
|
||||
if (selectedType == 0) { //pendant refine
|
||||
var matSet = [[4003004, 4030012, 4001356, 4000026], [4000026, 4001356, 4000073, 4001006], [4001343, 4011002, 4003004, 4003005], [4001343, 4011006, 4003004, 4003005], [4000091, 4011005, 4003004, 4003005], [4000091, 4011001, 4003004, 4003005], [4000469, 4011000, 4003004, 4003005], [4000469, 4011004, 4003004, 4003005], [1122007, 4003002, 4000413]];
|
||||
var matQtySet = [[20, 20, 5, 1], [5, 5, 10, 1], [10, 2, 20, 4], [10, 1, 20, 4], [15, 3, 30, 6], [15, 3, 30, 6], [20, 5, 20, 8], [20, 4, 40, 8], [1, 1, 1]];
|
||||
var costSet = [150000, 500000, 200000, 200000, 300000, 300000, 400000, 400000, 2500000];
|
||||
} else if (selectedType == 1) { //face accessory refine
|
||||
var matSet = [[4006000, 4003004], [4006000, 4003004, 4000026], [4006000, 4003004, 4000026, 4000082, 4003002], [4006000, 4003005], [4006000, 4003005, 4000026], [4006000, 4003005, 4000026, 4000082, 4003002], [4001006, 4011008], [4001006, 4011008], [4001006, 4011008], [4001006, 4011008]];
|
||||
var matQtySet = [[5, 5], [5, 5, 5], [5, 5, 5, 5, 1], [5, 5], [5, 5, 5], [5, 5, 5, 5, 1], [1, 1], [1, 1], [1, 1], [1, 1]];
|
||||
var costSet = [100000, 200000, 300000, 125000, 250000, 375000, 500000, 500000, 500000, 500000, 25000, 25000, 25000, 25000];
|
||||
} else if (selectedType == 2) { //eye accessory refine
|
||||
var matSet = [[4001006, 4003002, 4000082, 4031203], [4001005, 4011008], [4001005, 4011008], [4001005, 4011008, 4000082], [4001006, 4003002, 4003000, 4003001]];
|
||||
var matQtySet = [[2, 2, 5, 10], [3, 2], [4, 3], [5, 3, 10], [2, 2, 10, 5]];
|
||||
var costSet = [250000, 250000, 300000, 400000, 200000];
|
||||
} else if (selectedType == 3) { //belt & medals refine
|
||||
var matSet = [[4001006, 4003005, 4003004], [7777, 7777]];
|
||||
var matQtySet = [[2, 5, 10], [7777, 7777]];
|
||||
var costSet = [15000, 7777];
|
||||
} else if (selectedType == 4) { //ring refine
|
||||
var matSet = [[4003001, 4001344, 4006000], [4003001, 4001344, 4006000], [4021004, 4011008], [4011008, 4001006], [1112413, 2022039], [1112414, 4000176], [4011007, 4021009]];
|
||||
var matQtySet = [[2, 2, 2], [2, 2, 2], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]];
|
||||
var costSet = [10000, 10000, 10000, 20000, 15000, 15000, 10000];
|
||||
}/*else if (selectedType == 5) { //necklace refine
|
||||
var matSet = [[4011007, 4011008, 4021009]];
|
||||
var matQtySet = [[1, 1, 1]];
|
||||
var costSet = [10000];
|
||||
}*/
|
||||
|
||||
if (selectedType == 3) {
|
||||
selectedItem = Math.floor(Math.random() * maxEqp);
|
||||
item = items[selectedItem];
|
||||
mats = matSet[0];
|
||||
matQty = matQtySet[0];
|
||||
cost = costSet[0];
|
||||
} else {
|
||||
item = items[selectedItem];
|
||||
mats = matSet[selectedItem];
|
||||
matQty = matQtySet[selectedItem];
|
||||
cost = costSet[selectedItem];
|
||||
}
|
||||
|
||||
var prompt = "You want me to make ";
|
||||
if (selectedType != 3) {
|
||||
if (qty == 1) {
|
||||
prompt += "a #b#t" + item + "##k?";
|
||||
} else {
|
||||
prompt += "#b" + qty + " #t" + item + "##k?";
|
||||
}
|
||||
} else {
|
||||
prompt += "a #bbelt#k or a #bmedal#k?";
|
||||
}
|
||||
|
||||
prompt += " Right! I will need some items to make that item. Make sure you have a #bfree slot#k in your inventory!#b";
|
||||
if (mats instanceof Array) {
|
||||
for (var i = 0; i < mats.length; i++) {
|
||||
prompt += "\r\n#i" + mats[i] + "# " + (matQty[i] * qty) + " #t" + mats[i] + "#";
|
||||
}
|
||||
} else {
|
||||
prompt += "\r\n#i" + mats + "# " + (matQty * qty) + " #t" + mats + "#";
|
||||
}
|
||||
if (cost > 0) {
|
||||
prompt += "\r\n#i4031138# " + (cost * qty) + " meso";
|
||||
}
|
||||
cm.sendYesNo(prompt);
|
||||
} else if (status == 2) {
|
||||
if (cm.getMeso() < (cost * qty)) {
|
||||
cm.sendOk("This is the fee I charge to make my items! No credit.");
|
||||
} else {
|
||||
var complete = true;
|
||||
if (mats instanceof Array) {
|
||||
for (var i = 0; complete && i < mats.length; i++) {
|
||||
if (!cm.haveItem(mats[i], matQty[i] * qty)) {
|
||||
complete = false;
|
||||
}
|
||||
}
|
||||
} else if (!cm.haveItem(mats, matQty * qty)) {
|
||||
complete = false;
|
||||
}
|
||||
|
||||
if (!complete) {
|
||||
cm.sendOk("Are you sure you got all the items required? Double check it!");
|
||||
} else {
|
||||
if (cm.canHold(item, qty)) {
|
||||
if (mats instanceof Array) {
|
||||
for (var i = 0; i < mats.length; i++) {
|
||||
cm.gainItem(mats[i], -(matQty[i] * qty));
|
||||
}
|
||||
} else {
|
||||
cm.gainItem(mats, -(matQty * qty));
|
||||
}
|
||||
cm.gainMeso(-(cost * qty));
|
||||
|
||||
cm.gainItem(item, qty);
|
||||
cm.sendOk("The item is done! Take and try this piece of art yourself.");
|
||||
} else {
|
||||
cm.sendOk("You got no free slot on your inventory.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
cm.sendDefault();
|
||||
cm.dispose();
|
||||
}
|
||||
|
||||
@@ -19,72 +19,9 @@
|
||||
*/
|
||||
/* Dalair
|
||||
Medal NPC.
|
||||
|
||||
NPC Equipment Merger:
|
||||
* @author Ronan Lana
|
||||
*/
|
||||
|
||||
var status;
|
||||
var mergeFee = 50000;
|
||||
var name;
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1) {
|
||||
status++;
|
||||
} else {
|
||||
status--;
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (!YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
cm.sendOk("The medal ranking system is currently unavailable...");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
var levelLimit = !cm.getPlayer().isCygnus() ? 160 : 110;
|
||||
var selStr = "The medal ranking system is currently unavailable... Therefore, I am providing the #bEquipment Merge#k service! ";
|
||||
|
||||
const MakerProcessor = Java.type('client.processor.action.MakerProcessor');
|
||||
if (!YamlConfig.config.server.USE_STARTER_MERGE && (cm.getPlayer().getLevel() < levelLimit || MakerProcessor.getMakerSkillLevel(cm.getPlayer()) < 3)) {
|
||||
selStr += "However, you must have #rMaker level 3#k and at least #rlevel 110#k (Cygnus Knight), #rlevel 160#k (other classes) and a fund of #r" + cm.numberWithCommas(mergeFee) + " mesos#k to use the service.";
|
||||
cm.sendOk(selStr);
|
||||
cm.dispose();
|
||||
} else if (cm.getMeso() < mergeFee) {
|
||||
selStr += "I'm sorry, but this service tax is of #r" + cm.numberWithCommas(mergeFee) + " mesos#k, which it seems you unfortunately don't have right now... Please, stop by again later.";
|
||||
cm.sendOk(selStr);
|
||||
cm.dispose();
|
||||
} else {
|
||||
selStr += "For the fee of #r" + cm.numberWithCommas(mergeFee) + "#k mesos, merge unnecessary equipments in your inventory into your currently equipped gears to get stat boosts into them, statups based on the attributes of the items used on the merge!";
|
||||
cm.sendNext(selStr);
|
||||
}
|
||||
} else if (status == 1) {
|
||||
selStr = "#rWARNING#b: Make sure you have your items ready to merge at the slots #rAFTER#b the item you have selected to merge.#k Any items #bunder#k the item selected will be merged thoroughly.\r\n\r\nNote that equipments receiving bonuses from merge are going to become #rUntradeable#k thereon, and equipments that already received the merge bonus #rcannot be used for merge#k.\r\n\r\n";
|
||||
cm.sendGetText(selStr);
|
||||
} else if (status == 2) {
|
||||
name = cm.getText();
|
||||
|
||||
if (cm.getPlayer().mergeAllItemsFromName(name)) {
|
||||
cm.gainMeso(-mergeFee);
|
||||
cm.sendOk("Merging complete! Thanks for using the service and enjoy your new equipment stats.");
|
||||
} else {
|
||||
cm.sendOk("There is no #b'" + name + "'#k in your #bEQUIP#k inventory!");
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
cm.sendOk("The medal ranking system is currently unavailable...");
|
||||
cm.dispose();
|
||||
}
|
||||
|
||||
@@ -19,54 +19,9 @@
|
||||
*/
|
||||
/* NPC: Donation Box (9000041)
|
||||
Victoria Road : Henesys
|
||||
|
||||
NPC Bazaar:
|
||||
* @author Ronan Lana
|
||||
*/
|
||||
|
||||
var options = ["EQUIP", "USE", "SET-UP", "ETC"];
|
||||
var name;
|
||||
var status;
|
||||
var selectedType = 0;
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
cm.sendOk("The medal ranking system is currently unavailable...");
|
||||
cm.dispose();
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
status++;
|
||||
if (mode != 1) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (!YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
cm.sendOk("The medal ranking system is currently unavailable...");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
var selStr = "Hello, I am the #bBazaar NPC#k! Sell to me any item on your inventory you don't need. #rWARNING#b: Make sure you have your items ready to sell at the slots #rAFTER#b the item you have selected to sell.#k Any items #bunder#k the item selected will be sold thoroughly.";
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
selStr += "\r\n#L" + i + "# " + options[i] + "#l";
|
||||
}
|
||||
cm.sendSimple(selStr);
|
||||
} else if (status == 1) {
|
||||
selectedType = selection;
|
||||
cm.sendGetText("From what item on your #r" + options[selectedType] + "#k inventory do you want to start the transaction?");
|
||||
} else if (status == 2) {
|
||||
name = cm.getText();
|
||||
var res = cm.getPlayer().sellAllItemsFromName(selectedType + 1, name);
|
||||
|
||||
if (res > -1) {
|
||||
cm.sendOk("Transaction complete! You received #r" + cm.numberWithCommas(res) + " mesos#k from this action.");
|
||||
} else {
|
||||
cm.sendOk("There is no #b'" + name + "'#k in your #b" + options[selectedType] + "#k inventory!");
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ function action(mode, type, selection) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (cm.getMeso < price) {
|
||||
if (cm.getMeso() < price) {
|
||||
cm.sendOk("Please check and see if you have " + price + " mesos to enter this place.");
|
||||
} else {
|
||||
cm.gainMeso(-price);
|
||||
|
||||
@@ -4,13 +4,6 @@
|
||||
*/
|
||||
|
||||
function start() {
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
cm.openShopNPC(9201101);
|
||||
} else {
|
||||
//cm.sendOk("The patrol in New Leaf City is always ready. No creatures are able to break through to the city.");
|
||||
cm.sendDefault();
|
||||
}
|
||||
|
||||
cm.sendDefault();
|
||||
cm.dispose();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
var status;
|
||||
|
||||
var anthemSong = "Field/anthem/brazil"; // sound src: https://c7.rbxcdn.com/f91060652a6e9fbfbf92cb1418435448
|
||||
var ambientSong = "Bgm04/Shinin'Harbor";
|
||||
|
||||
var feature_tree = [];
|
||||
@@ -217,7 +216,6 @@ function writeFeatureTab_Serverpotentials() {
|
||||
addFeature("Poison damage value visible for other players.");
|
||||
addFeature("M. book announcer displays info based on demand.");
|
||||
addFeature("Custom jail system.");
|
||||
addFeature("Custom buyback system, uses mesos / NX, via MTS.");
|
||||
addFeature("Custom fishing system having 'seasonal' catch times.");
|
||||
addFeature("Actual fishing handling w/ F. Net - thanks Dragohe4rt!");
|
||||
addFeature("Custom map leasing system.");
|
||||
@@ -260,7 +258,7 @@ function writeFeatureTab_CustomNPCs() {
|
||||
function writeFeatureTab_Localhostedits() {
|
||||
addFeature("Removed the 'n' NPC dialog issue.");
|
||||
addFeature("Removed caps for MATK, WMDEF, ACC and AVOID.");
|
||||
addFeature("Removed MTS block, buyback available anywhere.");
|
||||
addFeature("Removed MTS block.");
|
||||
addFeature("Removed party blocks for novices under level 10.");
|
||||
addFeature("Set a much more higher cap for SPEED.");
|
||||
addFeature("Removed AP usage block for novices.");
|
||||
@@ -308,8 +306,6 @@ function writeAllFeatures() {
|
||||
}
|
||||
|
||||
function start() {
|
||||
const PacketCreator = Java.type('tools.PacketCreator');
|
||||
cm.getPlayer().sendPacket(PacketCreator.musicChange(anthemSong));
|
||||
status = -1;
|
||||
writeAllFeatures();
|
||||
action(1, 0, 0);
|
||||
@@ -369,4 +365,4 @@ function generateSelectionMenu(array) {
|
||||
menu += "#L" + i + "#" + array[i] + "#l\r\n";
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,37 +21,6 @@
|
||||
Default Maple TV
|
||||
*/
|
||||
|
||||
var status;
|
||||
|
||||
function start() {
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (YamlConfig.config.server.USE_ENABLE_CUSTOM_NPC_SCRIPT) {
|
||||
cm.dispose();
|
||||
cm.openNpc(9201088, "scroll_generator");
|
||||
return;
|
||||
}
|
||||
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1) {
|
||||
status++;
|
||||
} else {
|
||||
status--;
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
// do nothing
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
cm.dispose();
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
This file is part of the OdinMS Maple Story Server
|
||||
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
|
||||
Matthias Butz <matze@odinms.de>
|
||||
Jan Christian Meyer <vimes@odinms.de>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* Rebirth NPC
|
||||
@author Ronan
|
||||
@author wejrox
|
||||
*/
|
||||
var status;
|
||||
var jobId = 0;
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
|
||||
cm.sendOk("Rebirths aren't enabled on this server, how did you get here?");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode === 1) {
|
||||
status++;
|
||||
} else {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (status === 0) {
|
||||
cm.sendNext("Come to me when you want to be reborn again. You currently have a total of #r" + cm.getChar().getReborns() + " #krebirths.");
|
||||
} else if (status === 1) {
|
||||
cm.sendSimple("What do you want me to do today: \r\n \r\n #L0##bI want to be reborn!#l \r\n #L1##bNothing for now...#k#l");
|
||||
} else if (status === 2) {
|
||||
if (selection === 0) {
|
||||
if (cm.getChar().getLevel() === cm.getChar().getMaxClassLevel()) {
|
||||
cm.sendSimple("I see... and which path would you like to take? \r\n\r\n #L0##bExplorer (Beginner)#l \r\n #L1##bCygnus Knight (Noblesse)#l \r\n #L2##bAran (Legend)#l");
|
||||
} else {
|
||||
cm.sendOk("It looks like your journey has not yet ended... come back when you're level " + cm.getChar().getMaxClassLevel());
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (selection === 1) {
|
||||
cm.sendOk("See you soon!")
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (status === 3) {
|
||||
// 0 => beginner, 1000 => noblesse, 2000 => legend
|
||||
// makes this very easy :-)
|
||||
jobId = selection * 1000;
|
||||
|
||||
var job = "";
|
||||
if (selection === 0) job = "Beginner";
|
||||
else if (selection === 1) job = "Noblesse";
|
||||
else if (selection === 2) job = "Legend";
|
||||
cm.sendYesNo("Are you sure you want to be reborn as a " + job + "?");
|
||||
}
|
||||
else if (status === 4 && type === 1) {
|
||||
cm.getChar().executeRebornAsId(jobId);
|
||||
cm.sendOk("You have now been reborn. That's a total of #r" + cm.getChar().getReborns() + "#k rebirths");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,452 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* NPC: MapleTV / Larry
|
||||
|
||||
Exchanger NPC:
|
||||
* Scroll generator
|
||||
*
|
||||
* @author Ronan Lana
|
||||
*/
|
||||
|
||||
var status;
|
||||
|
||||
var jobWeaponRestricted = [[[2043000, 2043100, 2044000, 2044100, 2043200, 2044200]], [[2043000, 2043100, 2044000, 2044100], [2043000, 2043200, 2044000, 2044200], [2044300, 2044400]], [[2043700, 2043800], [2043700, 2043800], [2043700, 2043800]], [[2044500], [2044600]], [[2044700], [2043300]], [[2044800], [2044900]]];
|
||||
var aranWeaponRestricted = [jobWeaponRestricted[1][2][1]];
|
||||
|
||||
var tier1Scrolls = [];
|
||||
var tier2Scrolls = [2040000, 2040400, 2040500, 2040600, 2040700, 2040800, 2040900];
|
||||
var tier3Scrolls = [2048000, 2049200, 2041000, 2041100, 2041300, 2040100, 2040200, 2040300];
|
||||
|
||||
var typeTierScrolls = [["PAD", "MAD"], ["STR", "DEX", "INT", "LUK", "ACC", "EVA", "Speed", "Jump"], ["PDD", "MDD", "MHP", "MMP"]];
|
||||
|
||||
var sgItems = [4003004, 4003005, 4001006, 4006000, 4006001, 4030012];
|
||||
var sgToBucket = [100, 50, 37.5, 37.5, 37.5, 200];
|
||||
var mesoToBucket = 2800000;
|
||||
|
||||
var sgAppliedItems = [0, 0, 0, 0, 0, 0];
|
||||
var sgAppliedMeso = 0;
|
||||
|
||||
var sgBuckets = 0.0;
|
||||
var sgBookBuckets = 0.0;
|
||||
var sgItemBuckets = 0.0;
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1) {
|
||||
status++;
|
||||
} else {
|
||||
status--;
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
cm.sendNext("This is the MapleTV Scroll Generator broadcast. Place your supplies or mesos earned throughout your adventure to redeem a prize! You can place #bany amount of supplies#k, however take note that placing #rdifferent supplies#k with #rbigger shots of any of them#k will improve the reward possibilities!");
|
||||
} else if (status == 1) {
|
||||
var sendStr;
|
||||
|
||||
//print("Book: " + sgBookBuckets + " Item: " + sgItemBuckets);
|
||||
|
||||
if (sgItemBuckets > 0.0) {
|
||||
sendStr = "With the items you have currently placed, you have #r" + sgBuckets + "#k buckets (#r" + (sgItemBuckets < 1.0 ? sgItemBuckets.toFixed(2) : Math.floor(sgItemBuckets)) + "#k supply buckets) for claiming a prize. Place supplies:";
|
||||
} else {
|
||||
sendStr = "You have placed no supplies yet. Place supplies:";
|
||||
}
|
||||
|
||||
var listStr = "";
|
||||
var i;
|
||||
for (i = 0; i < sgItems.length; i++) {
|
||||
listStr += "#b#L" + i + "##t" + sgItems[i] + "##k";
|
||||
if (sgAppliedItems[i] > 0) {
|
||||
listStr += " - " + sgAppliedItems[i];
|
||||
}
|
||||
listStr += "#l\r\n";
|
||||
}
|
||||
|
||||
listStr += "#b#L" + i + "#Mesos#k";
|
||||
if (sgAppliedMeso > 0) {
|
||||
listStr += " - " + sgAppliedMeso;
|
||||
}
|
||||
listStr += "#l\r\n";
|
||||
|
||||
cm.sendSimple(sendStr + "\r\n\r\n" + listStr + "#r#L" + (sgItems.length + 2) + "#Retrieve a prize!#l#k\r\n");
|
||||
} else if (status == 2) {
|
||||
if (selection == (sgItems.length + 2)) {
|
||||
if (sgItemBuckets < 1.0) {
|
||||
cm.sendPrev("You have set not enough supplies. Insert at least one bucket of #bsupplies#k to claim a prize.");
|
||||
} else {
|
||||
generateRandomScroll();
|
||||
cm.dispose();
|
||||
}
|
||||
} else {
|
||||
var tickSel;
|
||||
if (selection < sgItems.length) {
|
||||
tickSel = "of #b#t" + sgItems[selection] + "##k";
|
||||
curItemQty = cm.getItemQuantity(sgItems[selection]);
|
||||
} else {
|
||||
tickSel = "#bmesos#k";
|
||||
curItemQty = cm.getMeso();
|
||||
}
|
||||
|
||||
curItemSel = selection;
|
||||
if (curItemQty > 0) {
|
||||
cm.sendGetText("How many " + tickSel + " do you want to provide? (#r" + curItemQty + "#k available)#k");
|
||||
} else {
|
||||
cm.sendPrev("You have got #rnone#k " + tickSel + " to provide for Scroll Generation. Click '#rBack#k' to return to the main interface.");
|
||||
}
|
||||
}
|
||||
} else if (status == 3) {
|
||||
var text = cm.getText();
|
||||
|
||||
try {
|
||||
var placedQty = parseInt(text);
|
||||
if (isNaN(placedQty) || placedQty < 0) {
|
||||
throw true;
|
||||
}
|
||||
|
||||
if (placedQty > curItemQty) {
|
||||
cm.sendPrev("You cannot insert the given amount of #r" + (curItemSel < sgItems.length ? "#t" + sgItems[curItemSel] + "#" : "mesos") + "#k (#r" + curItemQty + "#k available). Click '#rBack#k' to return to the main interface.");
|
||||
} else {
|
||||
if (curItemSel < sgItems.length) {
|
||||
sgApplyItem(curItemSel, placedQty);
|
||||
} else {
|
||||
sgApplyMeso(placedQty);
|
||||
}
|
||||
|
||||
cm.sendPrev("Operation succeeded. Click '#rBack#k' to return to the main interface.");
|
||||
}
|
||||
} catch (err) {
|
||||
cm.sendPrev("You must enter a positive number of supplies to insert. Click '#rBack#k' to return to the main interface.");
|
||||
}
|
||||
|
||||
status = 2;
|
||||
} else {
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getJobTierScrolls() {
|
||||
var scrolls = [];
|
||||
|
||||
var job = cm.getPlayer().getJob();
|
||||
var jobScrolls = jobWeaponRestricted[Math.floor(cm.getPlayer().getJobStyle().getId() / 100)];
|
||||
|
||||
const GameConstants = Java.type('constants.game.GameConstants');
|
||||
var jobBranch = GameConstants.getJobBranch(job);
|
||||
if (jobBranch >= 2) {
|
||||
Array.prototype.push.apply(scrolls, jobScrolls[Math.floor((job.getId() / 10) % 10) - 1]);
|
||||
} else {
|
||||
for (var i = 0; i < jobScrolls.length; i++) {
|
||||
Array.prototype.push.apply(scrolls, jobScrolls[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return scrolls;
|
||||
}
|
||||
|
||||
function getScrollTypePool(rewardTier) {
|
||||
var scrolls = [];
|
||||
switch (rewardTier) {
|
||||
case 1:
|
||||
if (cm.getPlayer().isAran()) {
|
||||
Array.prototype.push.apply(scrolls, aranWeaponRestricted);
|
||||
} else {
|
||||
Array.prototype.push.apply(scrolls, getJobTierScrolls());
|
||||
}
|
||||
|
||||
Array.prototype.push.apply(scrolls, tier1Scrolls);
|
||||
break;
|
||||
case 2:
|
||||
Array.prototype.push.apply(scrolls, tier2Scrolls);
|
||||
break;
|
||||
default:
|
||||
Array.prototype.push.apply(scrolls, tier3Scrolls);
|
||||
}
|
||||
|
||||
return scrolls;
|
||||
}
|
||||
|
||||
function getScrollTier(scrollStats) {
|
||||
for (var i = 0; i < typeTierScrolls.length; i++) {
|
||||
for (var j = 0; j < typeTierScrolls[i].length; j++) {
|
||||
if (scrollStats.get(typeTierScrolls[i][j]) > 0) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
function getScrollSuccessTier(scrollStats) {
|
||||
var prop = scrollStats.get("success");
|
||||
|
||||
const YamlConfig = Java.type('config.YamlConfig');
|
||||
if (prop > 90) {
|
||||
return 3;
|
||||
} else if (prop < 50) {
|
||||
return YamlConfig.config.server.SCROLL_CHANCE_ROLLS > 2 ? 2 : 1;
|
||||
} else {
|
||||
return YamlConfig.config.server.SCROLL_CHANCE_ROLLS > 2 ? 1 : 2;
|
||||
}
|
||||
}
|
||||
|
||||
function getAvailableScrollsPool(baseScrolls, rewardTier, successTier) {
|
||||
var scrolls = [];
|
||||
const ItemInformationProvider = Java.type('server.ItemInformationProvider');
|
||||
var ii = ItemInformationProvider.getInstance();
|
||||
|
||||
for (var i = 0; i < baseScrolls.length; i++) {
|
||||
for (var j = 0; j < 100; j++) {
|
||||
var scrollid = baseScrolls[i] + j;
|
||||
var scrollStats = ii.getEquipStats(scrollid);
|
||||
if (scrollStats != null && ii.getScrollReqs(scrollid).isEmpty()) {
|
||||
var scrollTier = getScrollTier(scrollStats);
|
||||
if (scrollTier == rewardTier && successTier == getScrollSuccessTier(scrollStats)) {
|
||||
scrolls.push(scrollid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scrolls;
|
||||
}
|
||||
|
||||
// passive tier buckets...
|
||||
|
||||
function getLevelTier(level) {
|
||||
return Math.floor((level - 1) / 15) + 1;
|
||||
}
|
||||
|
||||
function getPlayerCardTierPower() {
|
||||
var cardset = cm.getPlayer().getMonsterBook().getCardSet();
|
||||
var countTier = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
for (var iterator = cardset.iterator(); iterator.hasNext();) {
|
||||
var ce = iterator.next();
|
||||
|
||||
var cardid = ce.getKey();
|
||||
var ceTier = Math.floor(cardid / 1000) % 10;
|
||||
countTier[ceTier] += ce.getValue();
|
||||
|
||||
if (ceTier >= 8) { // is special card
|
||||
const LifeFactory = Java.type('server.life.LifeFactory');
|
||||
const ItemInformationProvider = Java.type('server.ItemInformationProvider');
|
||||
var mobLevel = LifeFactory.getMonsterLevel(ItemInformationProvider.getInstance().getCardMobId(cardid));
|
||||
var mobTier = getLevelTier(mobLevel) - 1;
|
||||
|
||||
countTier[mobTier] += (ce.getValue() * 1.2);
|
||||
}
|
||||
}
|
||||
|
||||
return countTier;
|
||||
}
|
||||
|
||||
function calculateMobBookTierBuckets(tierSize, playerCards, tier) {
|
||||
if (tier < 1) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
tier--; // started at 1
|
||||
var tierHitRate = playerCards[tier] / (tierSize[tier] * 5);
|
||||
if (tierHitRate > 0.5) {
|
||||
tierHitRate = 0.5;
|
||||
}
|
||||
|
||||
return tierHitRate * 4;
|
||||
}
|
||||
|
||||
function calculateMobBookBuckets() {
|
||||
var book = cm.getPlayer().getMonsterBook();
|
||||
var bookLevelMult = 0.9 + (0.1 * book.getBookLevel());
|
||||
|
||||
var playerLevelTier = getLevelTier(cm.getPlayer().getLevel());
|
||||
if (playerLevelTier > 8) {
|
||||
playerLevelTier = 8;
|
||||
}
|
||||
|
||||
const MonsterBook = Java.type('client.MonsterBook');
|
||||
var tierSize = MonsterBook.getCardTierSize();
|
||||
var playerCards = getPlayerCardTierPower();
|
||||
|
||||
var prevBuckets = calculateMobBookTierBuckets(tierSize, playerCards, playerLevelTier - 1);
|
||||
var currBuckets = calculateMobBookTierBuckets(tierSize, playerCards, playerLevelTier);
|
||||
|
||||
return (prevBuckets + currBuckets) * bookLevelMult;
|
||||
}
|
||||
|
||||
function recalcBuckets() {
|
||||
sgBookBuckets = calculateMobBookBuckets();
|
||||
sgItemBuckets = calculateSuppliesBuckets();
|
||||
|
||||
var buckets = sgBookBuckets + sgItemBuckets;
|
||||
if (buckets > 6.0) {
|
||||
sgBuckets = 6;
|
||||
} else {
|
||||
sgBuckets = Math.floor(buckets);
|
||||
}
|
||||
}
|
||||
|
||||
// variable buckets...
|
||||
|
||||
function sgApplyItem(idx, amount) {
|
||||
if (sgAppliedItems[idx] != amount) {
|
||||
sgAppliedItems[idx] = amount;
|
||||
recalcBuckets();
|
||||
}
|
||||
}
|
||||
|
||||
function sgApplyMeso(amount) {
|
||||
if (sgAppliedMeso != amount) {
|
||||
sgAppliedMeso = amount;
|
||||
recalcBuckets();
|
||||
}
|
||||
}
|
||||
|
||||
function calculateSuppliesBuckets() {
|
||||
var suppliesHitRate = 0.0;
|
||||
for (var i = 0; i < sgItems.length; i++) {
|
||||
suppliesHitRate += sgAppliedItems[i] / sgToBucket[i];
|
||||
}
|
||||
suppliesHitRate *= 2;
|
||||
|
||||
suppliesHitRate += (sgAppliedMeso / mesoToBucket);
|
||||
return suppliesHitRate;
|
||||
}
|
||||
|
||||
function calculateScrollTiers() {
|
||||
var buckets = sgBuckets;
|
||||
var tiers = [0, 0, 0];
|
||||
while (buckets > 0) {
|
||||
var pool = [];
|
||||
for (var i = 0; i < tiers.length; i++) {
|
||||
if (tiers[i] < 2) {
|
||||
pool.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
var rnd = pool[Math.floor(Math.random() * pool.length)];
|
||||
|
||||
tiers[rnd]++;
|
||||
buckets--;
|
||||
}
|
||||
|
||||
// normalize tiers
|
||||
for (var i = 0; i < tiers.length; i++) {
|
||||
tiers[i] = 3 - tiers[i];
|
||||
}
|
||||
|
||||
return tiers;
|
||||
}
|
||||
|
||||
function getRandomScrollFromTiers(tiers) {
|
||||
var typeTier = tiers[0], subtypeTier = tiers[1], successTier = tiers[2];
|
||||
var scrollTypePool = getScrollTypePool(typeTier);
|
||||
var scrollPool = getAvailableScrollsPool(scrollTypePool, subtypeTier, successTier);
|
||||
|
||||
if (scrollPool.length > 0) {
|
||||
return scrollPool[Math.floor(Math.random() * scrollPool.length)];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
function getRandomScrollFromRightPermutations(tiers) {
|
||||
for (var i = 2; i >= 0; i--) {
|
||||
for (var j = i - 1; j >= 0; j--) {
|
||||
if (tiers[i] >= 3) {
|
||||
break;
|
||||
} else if (tiers[j] > 1) {
|
||||
tiers[i]++;
|
||||
tiers[j]--;
|
||||
|
||||
var itemid = getRandomScrollFromTiers(tiers);
|
||||
if (itemid != -1) {
|
||||
return itemid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
function getRandomScroll(tiers) {
|
||||
var itemid = getRandomScrollFromTiers(tiers);
|
||||
if (itemid == -1) {
|
||||
// worst case shift-right permutations...
|
||||
itemid = getRandomScrollFromRightPermutations(tiers);
|
||||
}
|
||||
|
||||
return itemid;
|
||||
}
|
||||
|
||||
function performExchange(sgItemid, sgCount) {
|
||||
if (cm.getMeso() < sgAppliedMeso) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < sgItems.length; i++) {
|
||||
var itemid = sgItems[i];
|
||||
var count = sgAppliedItems[i];
|
||||
if (count > 0 && !cm.haveItem(itemid, count)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cm.gainMeso(-sgAppliedMeso);
|
||||
|
||||
for (var i = 0; i < sgItems.length; i++) {
|
||||
var itemid = sgItems[i];
|
||||
var count = sgAppliedItems[i];
|
||||
cm.gainItem(itemid, -count);
|
||||
}
|
||||
|
||||
cm.gainItem(sgItemid, sgCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
function generateRandomScroll() {
|
||||
const InventoryType = Java.type('client.inventory.InventoryType');
|
||||
if (cm.getPlayer().getInventory(InventoryType.USE).getNumFreeSlot() >= 1) {
|
||||
var itemid = getRandomScroll(calculateScrollTiers());
|
||||
if (itemid != -1) {
|
||||
if (performExchange(itemid, 1)) {
|
||||
cm.sendNext("Transaction accepted! You have received a #r#t" + itemid + "##k.");
|
||||
} else {
|
||||
cm.sendOk("Oh, it looks like some items are missing... Please double-check provided items in your inventory before trying to exchange.");
|
||||
}
|
||||
} else {
|
||||
cm.sendOk("Sorry for the inconvenience, but it seems there are no scrolls on store right now... Try again later.");
|
||||
}
|
||||
} else {
|
||||
cm.sendOk("Please look out for a slot available on your USE inventory before trying for a scroll.");
|
||||
}
|
||||
}
|
||||
@@ -50,8 +50,14 @@ function end(mode, type, selection) {
|
||||
status++;
|
||||
}
|
||||
|
||||
// TODO: there are 10 different riffs; quest2288/0 through quest2288/9.
|
||||
// One of the riffs should play randomly upon the death of Spirit of Rock, but there is currently no system in place to achieve that in a reasonable way.
|
||||
// Spirit of Rock (4300013) spawns an invisible mob on death (Spirit of Rock's Soul, 4300017) which was likely used in some clever way in GMS.
|
||||
// The map (103040430) has two scripts which could be useful: onFirstUserEnter=Depart_Boss_F_Enter and onUserEnter=Depart_BossEnter
|
||||
// Currently, the best hypothesis is that one of the map scripts registers some form of "mob spawn" action/script that runs once the invisible mob spawns.
|
||||
// The script would randomly pick one of the 10 riffs and then register it with all chrs on the map (to later be used by this quest 2293) and play it.
|
||||
if (status == 0) {
|
||||
qm.sendSimple("Here, I'll give you some samples. Please listen to them and choose one. Please listen carefully before making your choide.\r\n\
|
||||
qm.sendSimple("Here, I'll give you some samples. Please listen to them and choose one. Please listen carefully before making your choice.\r\n\
|
||||
\t#b#L1# Listen to song No. 1#l \r\n\
|
||||
\t#L2# Listen to Song No. 2#l \r\n\
|
||||
\t#L3# Listen to Song No. 3#l \r\n\
|
||||
@@ -67,7 +73,7 @@ function end(mode, type, selection) {
|
||||
qm.sendOk("Was it this?");
|
||||
status = -1;
|
||||
} else if (selection == 3) {
|
||||
qm.playSound("quest2293/Die");
|
||||
qm.playSound("quest2288/6");
|
||||
qm.sendOk("You heard that?");
|
||||
status = -1;
|
||||
} else if (selection == 4) {
|
||||
@@ -88,4 +94,4 @@ function end(mode, type, selection) {
|
||||
} else if (status == 3) {
|
||||
qm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,12 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
|
||||
public class BuddyList {
|
||||
public enum BuddyOperation {
|
||||
|
||||
@@ -24,8 +24,16 @@ package client;
|
||||
|
||||
import client.autoban.AutobanManager;
|
||||
import client.creator.CharacterFactoryRecipe;
|
||||
import client.inventory.*;
|
||||
import client.inventory.Equip.StatUpgrade;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Inventory;
|
||||
import client.inventory.InventoryProof;
|
||||
import client.inventory.InventoryType;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ItemFactory;
|
||||
import client.inventory.ModifyInventory;
|
||||
import client.inventory.Pet;
|
||||
import client.inventory.PetDataFactory;
|
||||
import client.inventory.WeaponType;
|
||||
import client.inventory.manipulator.CashIdGenerator;
|
||||
import client.inventory.manipulator.InventoryManipulator;
|
||||
import client.keybind.KeyBinding;
|
||||
@@ -40,7 +48,35 @@ import constants.id.ItemId;
|
||||
import constants.id.MapId;
|
||||
import constants.id.MobId;
|
||||
import constants.inventory.ItemConstants;
|
||||
import constants.skills.*;
|
||||
import constants.skills.Aran;
|
||||
import constants.skills.Beginner;
|
||||
import constants.skills.Bishop;
|
||||
import constants.skills.BlazeWizard;
|
||||
import constants.skills.Bowmaster;
|
||||
import constants.skills.Brawler;
|
||||
import constants.skills.Buccaneer;
|
||||
import constants.skills.Corsair;
|
||||
import constants.skills.Crusader;
|
||||
import constants.skills.DarkKnight;
|
||||
import constants.skills.DawnWarrior;
|
||||
import constants.skills.Evan;
|
||||
import constants.skills.FPArchMage;
|
||||
import constants.skills.Hermit;
|
||||
import constants.skills.Hero;
|
||||
import constants.skills.ILArchMage;
|
||||
import constants.skills.Legend;
|
||||
import constants.skills.Magician;
|
||||
import constants.skills.Marauder;
|
||||
import constants.skills.Marksman;
|
||||
import constants.skills.NightLord;
|
||||
import constants.skills.Noblesse;
|
||||
import constants.skills.Paladin;
|
||||
import constants.skills.Priest;
|
||||
import constants.skills.Ranger;
|
||||
import constants.skills.Shadower;
|
||||
import constants.skills.Sniper;
|
||||
import constants.skills.ThunderBreaker;
|
||||
import constants.skills.Warrior;
|
||||
import net.packet.Packet;
|
||||
import net.server.PlayerBuffValueHolder;
|
||||
import net.server.PlayerCoolDownValueHolder;
|
||||
@@ -52,38 +88,98 @@ import net.server.guild.GuildCharacter;
|
||||
import net.server.guild.GuildPackets;
|
||||
import net.server.services.task.world.CharacterSaveService;
|
||||
import net.server.services.type.WorldServices;
|
||||
import net.server.world.*;
|
||||
import net.server.world.Messenger;
|
||||
import net.server.world.MessengerCharacter;
|
||||
import net.server.world.Party;
|
||||
import net.server.world.PartyCharacter;
|
||||
import net.server.world.PartyOperation;
|
||||
import net.server.world.World;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import scripting.AbstractPlayerInteraction;
|
||||
import scripting.event.EventInstanceManager;
|
||||
import scripting.item.ItemScriptManager;
|
||||
import server.*;
|
||||
import server.CashShop;
|
||||
import server.ExpLogger;
|
||||
import server.ExpLogger.ExpLogRecord;
|
||||
import server.ItemInformationProvider;
|
||||
import server.ItemInformationProvider.ScriptedItem;
|
||||
import server.Marriage;
|
||||
import server.Shop;
|
||||
import server.StatEffect;
|
||||
import server.Storage;
|
||||
import server.ThreadManager;
|
||||
import server.TimerManager;
|
||||
import server.Trade;
|
||||
import server.events.Events;
|
||||
import server.events.RescueGaga;
|
||||
import server.events.gm.Fitness;
|
||||
import server.events.gm.Ola;
|
||||
import server.life.*;
|
||||
import server.maps.*;
|
||||
import server.life.BanishInfo;
|
||||
import server.life.MobSkill;
|
||||
import server.life.MobSkillFactory;
|
||||
import server.life.MobSkillId;
|
||||
import server.life.MobSkillType;
|
||||
import server.life.Monster;
|
||||
import server.life.PlayerNPC;
|
||||
import server.maps.AbstractAnimatedMapObject;
|
||||
import server.maps.Door;
|
||||
import server.maps.DoorObject;
|
||||
import server.maps.Dragon;
|
||||
import server.maps.FieldLimit;
|
||||
import server.maps.HiredMerchant;
|
||||
import server.maps.MapEffect;
|
||||
import server.maps.MapItem;
|
||||
import server.maps.MapManager;
|
||||
import server.maps.MapObject;
|
||||
import server.maps.MapObjectType;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MiniGame;
|
||||
import server.maps.MiniGame.MiniGameResult;
|
||||
import server.maps.PlayerShop;
|
||||
import server.maps.PlayerShopItem;
|
||||
import server.maps.Portal;
|
||||
import server.maps.SavedLocation;
|
||||
import server.maps.SavedLocationType;
|
||||
import server.maps.Summon;
|
||||
import server.minigame.RockPaperScissor;
|
||||
import server.partyquest.AriantColiseum;
|
||||
import server.partyquest.MonsterCarnival;
|
||||
import server.partyquest.MonsterCarnivalParty;
|
||||
import server.partyquest.PartyQuest;
|
||||
import server.quest.Quest;
|
||||
import tools.*;
|
||||
import tools.exceptions.NotEnabledException;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.LongTool;
|
||||
import tools.PacketCreator;
|
||||
import tools.Pair;
|
||||
import tools.Randomizer;
|
||||
import tools.packets.WeddingPackets;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.sql.*;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -93,11 +189,12 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.*;
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
public class Character extends AbstractCharacterObject {
|
||||
private static final Logger log = LoggerFactory.getLogger(Character.class);
|
||||
private static final ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
private static final String LEVEL_200 = "[Congrats] %s has reached Level %d! Congratulate %s on such an amazing achievement!";
|
||||
private static final String[] BLOCKED_NAMES = {"admin", "owner", "moderator", "intern", "donor", "administrator", "FREDRICK", "help", "helper", "alert", "notice", "maplestory", "fuck", "wizet", "fucking", "negro", "fuk", "fuc", "penis", "pussy", "asshole", "gay",
|
||||
"nigger", "homo", "suck", "cum", "shit", "shitty", "condom", "security", "official", "rape", "nigga", "sex", "tit", "boner", "orgy", "clit", "asshole", "fatass", "bitch", "support", "gamemaster", "cock", "gaay", "gm",
|
||||
@@ -128,7 +225,7 @@ public class Character extends AbstractCharacterObject {
|
||||
private int expRate = 1, mesoRate = 1, dropRate = 1, expCoupon = 1, mesoCoupon = 1, dropCoupon = 1;
|
||||
private int omokwins, omokties, omoklosses, matchcardwins, matchcardties, matchcardlosses;
|
||||
private int owlSearch;
|
||||
private long lastfametime, lastUsedCashItem, lastExpression = 0, lastHealed, lastBuyback = 0, lastDeathtime, jailExpiration = -1;
|
||||
private long lastfametime, lastUsedCashItem, lastExpression = 0, lastHealed, lastDeathtime, jailExpiration = -1;
|
||||
private transient int localstr, localdex, localluk, localint_, localmagic, localwatk;
|
||||
private transient int equipmaxhp, equipmaxmp, equipstr, equipdex, equipluk, equipint_, equipmagic, equipwatk, localchairhp, localchairmp;
|
||||
private int localchairrate;
|
||||
@@ -151,6 +248,7 @@ public class Character extends AbstractCharacterObject {
|
||||
private final AtomicInteger gachaexp = new AtomicInteger();
|
||||
private final AtomicInteger meso = new AtomicInteger();
|
||||
private final AtomicInteger chair = new AtomicInteger(-1);
|
||||
private long totalExpGained = 0;
|
||||
private int merchantmeso;
|
||||
private BuddyList buddylist;
|
||||
private EventInstanceManager eventInstance = null;
|
||||
@@ -168,7 +266,7 @@ public class Character extends AbstractCharacterObject {
|
||||
private final Pet[] pets = new Pet[3];
|
||||
private PlayerShop playerShop = null;
|
||||
private Shop shop = null;
|
||||
private SkinColor skinColor = SkinColor.NORMAL;
|
||||
private SkinColor skinColor = SkinColor.LIGHT;
|
||||
private Storage storage = null;
|
||||
private Trade trade = null;
|
||||
private MonsterBook monsterbook;
|
||||
@@ -253,9 +351,6 @@ public class Character extends AbstractCharacterObject {
|
||||
private int targetHpBarHash = 0;
|
||||
private long targetHpBarTime = 0;
|
||||
private long nextWarningTime = 0;
|
||||
private int banishMap = -1;
|
||||
private int banishSp = -1;
|
||||
private long banishTime = 0;
|
||||
private long lastExpGainTime;
|
||||
private boolean pendingNameChange; //only used to change name on logout, not to be relied upon elsewhere
|
||||
private long loginTime;
|
||||
@@ -717,7 +812,7 @@ public class Character extends AbstractCharacterObject {
|
||||
int maxbasedamage;
|
||||
Item weapon_item = getInventory(InventoryType.EQUIPPED).getItem((short) -11);
|
||||
if (weapon_item != null) {
|
||||
maxbasedamage = calculateMaxBaseDamage(watk, ii.getWeaponType(weapon_item.getItemId()));
|
||||
maxbasedamage = calculateMaxBaseDamage(watk, ItemInformationProvider.getInstance().getWeaponType(weapon_item.getItemId()));
|
||||
} else {
|
||||
if (job.isA(Job.PIRATE) || job.isA(Job.THUNDERBREAKER1)) {
|
||||
double weapMulti = 3;
|
||||
@@ -817,7 +912,7 @@ public class Character extends AbstractCharacterObject {
|
||||
String medal = "";
|
||||
final Item medalItem = getInventory(InventoryType.EQUIPPED).getItem((short) -49);
|
||||
if (medalItem != null) {
|
||||
medal = "<" + ii.getName(medalItem.getItemId()) + "> ";
|
||||
medal = "<" + ItemInformationProvider.getInstance().getName(medalItem.getItemId()) + "> ";
|
||||
}
|
||||
return medal;
|
||||
}
|
||||
@@ -1257,48 +1352,14 @@ public class Character extends AbstractCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canRecoverLastBanish() {
|
||||
return System.currentTimeMillis() - this.banishTime < MINUTES.toMillis(5);
|
||||
}
|
||||
|
||||
public Pair<Integer, Integer> getLastBanishData() {
|
||||
return new Pair<>(this.banishMap, this.banishSp);
|
||||
}
|
||||
|
||||
public void clearBanishPlayerData() {
|
||||
this.banishMap = -1;
|
||||
this.banishSp = -1;
|
||||
this.banishTime = 0;
|
||||
}
|
||||
|
||||
public void setBanishPlayerData(int banishMap, int banishSp, long banishTime) {
|
||||
this.banishMap = banishMap;
|
||||
this.banishSp = banishSp;
|
||||
this.banishTime = banishTime;
|
||||
}
|
||||
|
||||
public void changeMapBanish(int mapid, String portal, String msg) {
|
||||
if (YamlConfig.config.server.USE_SPIKES_AVOID_BANISH) {
|
||||
for (Item it : this.getInventory(InventoryType.EQUIPPED).list()) {
|
||||
if ((it.getFlag() & ItemConstants.SPIKES) == ItemConstants.SPIKES) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int banMap = this.getMapId();
|
||||
int banSp = this.getMap().findClosestPlayerSpawnpoint(this.getPosition()).getId();
|
||||
long banTime = System.currentTimeMillis();
|
||||
|
||||
if (msg != null) {
|
||||
dropMessage(5, msg);
|
||||
public void changeMapBanish(BanishInfo banishInfo) {
|
||||
if (banishInfo.msg() != null) {
|
||||
dropMessage(5, banishInfo.msg());
|
||||
}
|
||||
|
||||
MapleMap map_ = getWarpMap(mapid);
|
||||
Portal portal_ = map_.getPortal(portal);
|
||||
Portal portal_ = map_.getPortal(banishInfo.portal());
|
||||
changeMap(map_, portal_ != null ? portal_ : map_.getRandomPlayerSpawnpoint());
|
||||
|
||||
setBanishPlayerData(banMap, banSp, banTime);
|
||||
}
|
||||
|
||||
public void changeMap(int map) {
|
||||
@@ -1681,7 +1742,6 @@ public class Character extends AbstractCharacterObject {
|
||||
this.mapTransitioning.set(true);
|
||||
|
||||
this.unregisterChairBuff();
|
||||
this.clearBanishPlayerData();
|
||||
Trade.cancelTrade(this, Trade.TradeResult.UNSUCCESSFUL_ANOTHER_MAP);
|
||||
this.closePlayerInteractions();
|
||||
|
||||
@@ -1873,6 +1933,7 @@ public class Character extends AbstractCharacterObject {
|
||||
|
||||
public boolean applyConsumeOnPickup(final int itemId) {
|
||||
if (itemId / 1000000 == 2) {
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
if (ii.isConsumeOnPickup(itemId)) {
|
||||
if (ItemConstants.isPartyItem(itemId)) {
|
||||
List<Character> partyMembers = this.getPartyMembersOnSameMap();
|
||||
@@ -1943,6 +2004,7 @@ public class Character extends AbstractCharacterObject {
|
||||
|
||||
Item mItem = mapitem.getItem();
|
||||
boolean hasSpaceInventory = true;
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
if (ItemId.isNxCard(mapitem.getItemId()) || mapitem.getMeso() > 0 || ii.isConsumeOnPickup(mapitem.getItemId()) || (hasSpaceInventory = InventoryManipulator.checkSpace(client, mapitem.getItemId(), mItem.getQuantity(), mItem.getOwner()))) {
|
||||
int mapId = this.getMapId();
|
||||
|
||||
@@ -1967,7 +2029,7 @@ public class Character extends AbstractCharacterObject {
|
||||
this.getCashShop().gainCash(1, nxGain);
|
||||
|
||||
if (YamlConfig.config.server.USE_ANNOUNCE_NX_COUPON_LOOT) {
|
||||
showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(1) + " NX)", 300);
|
||||
showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(CashShop.NX_CREDIT) + " NX)", 300);
|
||||
}
|
||||
|
||||
this.getMap().pickItemDrop(pickupPacket, mapitem);
|
||||
@@ -2019,7 +2081,7 @@ public class Character extends AbstractCharacterObject {
|
||||
this.getCashShop().gainCash(1, nxGain);
|
||||
|
||||
if (YamlConfig.config.server.USE_ANNOUNCE_NX_COUPON_LOOT) {
|
||||
showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(1) + " NX)", 300);
|
||||
showHint("You have earned #e#b" + nxGain + " NX#k#n. (" + this.getCashShop().getCash(CashShop.NX_CREDIT) + " NX)", 300);
|
||||
}
|
||||
} else if (applyConsumeOnPickup(mItem.getItemId())) {
|
||||
} else if (InventoryManipulator.addFromDrop(client, mItem, true)) {
|
||||
@@ -2061,6 +2123,7 @@ public class Character extends AbstractCharacterObject {
|
||||
}
|
||||
|
||||
public boolean canHoldUniques(List<Integer> itemids) {
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
for (Integer itemid : itemids) {
|
||||
if (ii.isPickupRestricted(itemid) && this.haveItem(itemid)) {
|
||||
return false;
|
||||
@@ -3090,6 +3153,7 @@ public class Character extends AbstractCharacterObject {
|
||||
leftover = nextExp - Integer.MAX_VALUE;
|
||||
}
|
||||
updateSingleStat(Stat.EXP, exp.addAndGet((int) total));
|
||||
totalExpGained += total;
|
||||
if (show) {
|
||||
announceExpGain(gain, equip, party, inChat, white);
|
||||
}
|
||||
@@ -3106,6 +3170,20 @@ public class Character extends AbstractCharacterObject {
|
||||
gainExpInternal(leftover, equip, party, false, inChat, white);
|
||||
} else {
|
||||
lastExpGainTime = System.currentTimeMillis();
|
||||
|
||||
if (YamlConfig.config.server.USE_EXP_GAIN_LOG) {
|
||||
ExpLogRecord expLogRecord = new ExpLogger.ExpLogRecord(
|
||||
getWorldServer().getExpRate(),
|
||||
expCoupon,
|
||||
totalExpGained,
|
||||
exp.get(),
|
||||
new Timestamp(lastExpGainTime),
|
||||
id
|
||||
);
|
||||
ExpLogger.putExpLogRecord(expLogRecord);
|
||||
}
|
||||
|
||||
totalExpGained = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3740,6 +3818,7 @@ public class Character extends AbstractCharacterObject {
|
||||
}
|
||||
|
||||
public void cancelEffect(int itemId) {
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
cancelEffect(ii.getItemEffect(itemId), false, -1);
|
||||
}
|
||||
|
||||
@@ -5945,7 +6024,8 @@ public class Character extends AbstractCharacterObject {
|
||||
sendPacket(PacketCreator.giveBuff(energybar, 0, stat));
|
||||
sendPacket(PacketCreator.showOwnBuffEffect(energycharge.getId(), 2));
|
||||
getMap().broadcastPacket(this, PacketCreator.showBuffEffect(id, energycharge.getId(), 2));
|
||||
getMap().broadcastPacket(this, PacketCreator.giveForeignBuff(energybar, stat));
|
||||
getMap().broadcastPacket(this, PacketCreator.giveForeignPirateBuff(id, energycharge.getId(),
|
||||
ceffect.getDuration(), stat));
|
||||
}
|
||||
if (energybar >= 10000 && energybar < 11000) {
|
||||
energybar = 15000;
|
||||
@@ -6034,65 +6114,6 @@ public class Character extends AbstractCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canBuyback(int fee, boolean usingMesos) {
|
||||
return (usingMesos ? this.getMeso() : cashshop.getCash(1)) >= fee;
|
||||
}
|
||||
|
||||
private void applyBuybackFee(int fee, boolean usingMesos) {
|
||||
if (usingMesos) {
|
||||
this.gainMeso(-fee);
|
||||
} else {
|
||||
cashshop.gainCash(1, -fee);
|
||||
}
|
||||
}
|
||||
|
||||
private long getNextBuybackTime() {
|
||||
return lastBuyback + MINUTES.toMillis(YamlConfig.config.server.BUYBACK_COOLDOWN_MINUTES);
|
||||
}
|
||||
|
||||
private boolean isBuybackInvincible() {
|
||||
return Server.getInstance().getCurrentTime() - lastBuyback < 4200;
|
||||
}
|
||||
|
||||
private int getBuybackFee() {
|
||||
float fee = YamlConfig.config.server.BUYBACK_FEE;
|
||||
int grade = Math.min(Math.max(level, 30), 120) - 30;
|
||||
|
||||
fee += (grade * YamlConfig.config.server.BUYBACK_LEVEL_STACK_FEE);
|
||||
if (YamlConfig.config.server.USE_BUYBACK_WITH_MESOS) {
|
||||
fee *= YamlConfig.config.server.BUYBACK_MESO_MULTIPLIER;
|
||||
}
|
||||
|
||||
return (int) Math.floor(fee);
|
||||
}
|
||||
|
||||
public void showBuybackInfo() {
|
||||
String s = "#eBUYBACK STATUS#n\r\n\r\nCurrent buyback fee: #b" + getBuybackFee() + " " + (YamlConfig.config.server.USE_BUYBACK_WITH_MESOS ? "mesos" : "NX") + "#k\r\n\r\n";
|
||||
|
||||
long timeNow = Server.getInstance().getCurrentTime();
|
||||
boolean avail = true;
|
||||
if (!isAlive()) {
|
||||
long timeLapsed = timeNow - lastDeathtime;
|
||||
long timeRemaining = MINUTES.toMillis(YamlConfig.config.server.BUYBACK_RETURN_MINUTES) - (timeLapsed + Math.max(0, getNextBuybackTime() - timeNow));
|
||||
if (timeRemaining < 1) {
|
||||
s += "Buyback #e#rUNAVAILABLE#k#n";
|
||||
avail = false;
|
||||
} else {
|
||||
s += "Buyback countdown: #e#b" + getTimeRemaining(MINUTES.toMillis(YamlConfig.config.server.BUYBACK_RETURN_MINUTES) - timeLapsed) + "#k#n";
|
||||
}
|
||||
s += "\r\n";
|
||||
}
|
||||
|
||||
if (timeNow < getNextBuybackTime() && avail) {
|
||||
s += "Buyback available in #r" + getTimeRemaining(getNextBuybackTime() - timeNow) + "#k";
|
||||
s += "\r\n";
|
||||
} else {
|
||||
s += "Buyback #bavailable#k";
|
||||
}
|
||||
|
||||
this.showHint(s);
|
||||
}
|
||||
|
||||
private static String getTimeRemaining(long timeLeft) {
|
||||
int seconds = (int) Math.floor(timeLeft / SECONDS.toMillis(1)) % 60;
|
||||
int minutes = (int) Math.floor(timeLeft / MINUTES.toMillis(1)) % 60;
|
||||
@@ -6100,34 +6121,6 @@ public class Character extends AbstractCharacterObject {
|
||||
return (minutes > 0 ? (String.format("%02d", minutes) + " minutes, ") : "") + String.format("%02d", seconds) + " seconds";
|
||||
}
|
||||
|
||||
public boolean couldBuyback() { // Ronan's buyback system
|
||||
long timeNow = Server.getInstance().getCurrentTime();
|
||||
|
||||
if (timeNow - lastDeathtime > MINUTES.toMillis(YamlConfig.config.server.BUYBACK_RETURN_MINUTES)) {
|
||||
this.dropMessage(5, "The period of time to decide has expired, therefore you are unable to buyback.");
|
||||
return false;
|
||||
}
|
||||
|
||||
long nextBuybacktime = getNextBuybackTime();
|
||||
if (timeNow < nextBuybacktime) {
|
||||
long timeLeft = nextBuybacktime - timeNow;
|
||||
this.dropMessage(5, "Next buyback available in " + getTimeRemaining(timeLeft) + ".");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean usingMesos = YamlConfig.config.server.USE_BUYBACK_WITH_MESOS;
|
||||
int fee = getBuybackFee();
|
||||
|
||||
if (!canBuyback(fee, usingMesos)) {
|
||||
this.dropMessage(5, "You don't have " + fee + " " + (usingMesos ? "mesos" : "NX") + " to buyback.");
|
||||
return false;
|
||||
}
|
||||
|
||||
lastBuyback = timeNow;
|
||||
applyBuybackFee(fee, usingMesos);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isBuffFrom(BuffStat stat, Skill skill) {
|
||||
effLock.lock();
|
||||
chrLock.lock();
|
||||
@@ -6479,7 +6472,6 @@ public class Character extends AbstractCharacterObject {
|
||||
ThreadManager.getInstance().newTask(r);
|
||||
}
|
||||
|
||||
levelUpMessages();
|
||||
guildUpdate();
|
||||
|
||||
FamilyEntry familyEntry = getFamilyEntry();
|
||||
@@ -6518,94 +6510,6 @@ public class Character extends AbstractCharacterObject {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void levelUpMessages() {
|
||||
if (level % 5 != 0) { //Performance FTW?
|
||||
return;
|
||||
}
|
||||
if (level == 5) {
|
||||
yellowMessage("Aww, you're level 5, how cute!");
|
||||
} else if (level == 10) {
|
||||
yellowMessage("Henesys Party Quest is now open to you! Head over to Henesys, find some friends, and try it out!");
|
||||
} else if (level == 15) {
|
||||
yellowMessage("Half-way to your 2nd job advancement, nice work!");
|
||||
} else if (level == 20) {
|
||||
yellowMessage("You can almost Kerning Party Quest!");
|
||||
} else if (level == 25) {
|
||||
yellowMessage("You seem to be improving, but you are still not ready to move on to the next step.");
|
||||
} else if (level == 30) {
|
||||
yellowMessage("You have finally reached level 30! Try job advancing, after that try the Mushroom Castle!");
|
||||
} else if (level == 35) {
|
||||
yellowMessage("Hey did you hear about this mall that opened in Kerning? Try visiting the Kerning Mall.");
|
||||
} else if (level == 40) {
|
||||
yellowMessage("Do @rates to see what all your rates are!");
|
||||
} else if (level == 45) {
|
||||
yellowMessage("I heard that a rock and roll artist died during the grand opening of the Kerning Mall. People are naming him the Spirit of Rock.");
|
||||
} else if (level == 50) {
|
||||
yellowMessage("You seem to be growing very fast, would you like to test your new found strength with the mighty Zakum?");
|
||||
} else if (level == 55) {
|
||||
yellowMessage("You can now try out the Ludibrium Maze Party Quest!");
|
||||
} else if (level == 60) {
|
||||
yellowMessage("Feels good to be near the end of 2nd job, doesn't it?");
|
||||
} else if (level == 65) {
|
||||
yellowMessage("You're only 5 more levels away from 3rd job, not bad!");
|
||||
} else if (level == 70) {
|
||||
yellowMessage("I see many people wearing a teddy bear helmet. I should ask someone where they got it from.");
|
||||
} else if (level == 75) {
|
||||
yellowMessage("You have reached level 3 quarters!");
|
||||
} else if (level == 80) {
|
||||
yellowMessage("You think you are powerful enough? Try facing horntail!");
|
||||
} else if (level == 85) {
|
||||
yellowMessage("Did you know? The majority of people who hit level 85 in Cosmic don't live to be 85 years old?");
|
||||
} else if (level == 90) {
|
||||
yellowMessage("Hey do you like the amusement park? I heard Spooky World is the best theme park around. I heard they sell cute teddy-bears.");
|
||||
} else if (level == 95) {
|
||||
yellowMessage("100% of people who hit level 95 in Cosmic don't live to be 95 years old.");
|
||||
} else if (level == 100) {
|
||||
yellowMessage("Mid-journey so far... You just reached level 100! Now THAT's such a feat, however to manage the 200 you will need even more passion and determination than ever! Good hunting!");
|
||||
} else if (level == 105) {
|
||||
yellowMessage("Have you ever been to leafre? I heard they have dragons!");
|
||||
} else if (level == 110) {
|
||||
yellowMessage("I see many people wearing a teddy bear helmet. I should ask someone where they got it from.");
|
||||
} else if (level == 115) {
|
||||
yellowMessage("I bet all you can think of is level 120, huh? Level 115 gets no love.");
|
||||
} else if (level == 120) {
|
||||
yellowMessage("Are you ready to learn from the masters? Head over to your job instructor!");
|
||||
} else if (level == 125) {
|
||||
yellowMessage("The struggle for mastery books has begun, huh?");
|
||||
} else if (level == 130) {
|
||||
yellowMessage("You should try Temple of Time. It should be pretty decent EXP.");
|
||||
} else if (level == 135) {
|
||||
yellowMessage("I hope you're still not struggling for mastery books!");
|
||||
} else if (level == 140) {
|
||||
yellowMessage("You're well into 4th job at this point, great work!");
|
||||
} else if (level == 145) {
|
||||
yellowMessage("Level 145 is serious business!");
|
||||
} else if (level == 150) {
|
||||
yellowMessage("You have becomed quite strong, but the journey is not yet over.");
|
||||
} else if (level == 155) {
|
||||
yellowMessage("At level 155, Zakum should be a joke to you. Nice job!");
|
||||
} else if (level == 160) {
|
||||
yellowMessage("Level 160 is pretty impressive. Try taking a picture and putting it on Instagram.");
|
||||
} else if (level == 165) {
|
||||
yellowMessage("At this level, you should start looking into doing some boss runs.");
|
||||
} else if (level == 170) {
|
||||
yellowMessage("Level 170, huh? You have the heart of a champion.");
|
||||
} else if (level == 175) {
|
||||
yellowMessage("You came a long way from level 1. Amazing job so far.");
|
||||
} else if (level == 180) {
|
||||
yellowMessage("Have you ever tried taking a boss on by yourself? It is quite difficult.");
|
||||
} else if (level == 185) {
|
||||
yellowMessage("Legend has it that you're a legend.");
|
||||
} else if (level == 190) {
|
||||
yellowMessage("You only have 10 more levels to go until you hit 200!");
|
||||
} else if (level == 195) {
|
||||
yellowMessage("Nothing is stopping you at this point, level 195!");
|
||||
} else if (level == 200) {
|
||||
yellowMessage("Very nicely done! You have reached the so-long dreamed LEVEL 200!!! You are truly a hero among men, cheers upon you!");
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlayerRates() {
|
||||
this.expRate *= GameConstants.getPlayerBonusExpRate(this.level / 20);
|
||||
this.mesoRate *= GameConstants.getPlayerBonusMesoRate(this.level / 20);
|
||||
@@ -6793,6 +6697,7 @@ public class Character extends AbstractCharacterObject {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
StatEffect mse = ii.getItemEffect(couponid);
|
||||
mse.applyTo(this);
|
||||
}
|
||||
@@ -7825,6 +7730,7 @@ public class Character extends AbstractCharacterObject {
|
||||
if (job.isA(Job.THIEF) || job.isA(Job.BOWMAN) || job.isA(Job.PIRATE) || job.isA(Job.NIGHTWALKER1) || job.isA(Job.WINDARCHER1)) {
|
||||
Item weapon_item = getInventory(InventoryType.EQUIPPED).getItem((short) -11);
|
||||
if (weapon_item != null) {
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
WeaponType weapon = ii.getWeaponType(weapon_item.getItemId());
|
||||
boolean bow = weapon == WeaponType.BOW;
|
||||
boolean crossbow = weapon == WeaponType.CROSSBOW;
|
||||
@@ -8792,22 +8698,6 @@ public class Character extends AbstractCharacterObject {
|
||||
return skillMacros;
|
||||
}
|
||||
|
||||
public void sendNote(String to, String msg, byte fame) throws SQLException {
|
||||
sendNote(to, this.getName(), msg, fame);
|
||||
}
|
||||
|
||||
public static void sendNote(String to, String from, String msg, byte fame) throws SQLException {
|
||||
try (Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO notes (`to`, `from`, `message`, `timestamp`, `fame`) VALUES (?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS)) {
|
||||
ps.setString(1, to);
|
||||
ps.setString(2, from);
|
||||
ps.setString(3, msg);
|
||||
ps.setLong(4, Server.getInstance().getCurrentTime());
|
||||
ps.setByte(5, fame);
|
||||
ps.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAriantRoomLeader(int room, String charname) {
|
||||
ariantroomleader[room] = charname;
|
||||
}
|
||||
@@ -8994,11 +8884,7 @@ public class Character extends AbstractCharacterObject {
|
||||
boolean playerDied = false;
|
||||
if (hp <= 0) {
|
||||
if (oldHp > hp) {
|
||||
if (!isBuybackInvincible()) {
|
||||
playerDied = true;
|
||||
} else {
|
||||
hp = 1;
|
||||
}
|
||||
playerDied = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9219,20 +9105,6 @@ public class Character extends AbstractCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public void changeName(String name) {
|
||||
FredrickProcessor.removeFredrickReminders(this.getId());
|
||||
|
||||
this.name = name;
|
||||
try (Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("UPDATE `characters` SET `name` = ? WHERE `id` = ?")) {
|
||||
ps.setString(1, name);
|
||||
ps.setInt(2, id);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public int getDoorSlot() {
|
||||
if (doorSlot != -1) {
|
||||
return doorSlot;
|
||||
@@ -9319,242 +9191,6 @@ public class Character extends AbstractCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public int sellAllItemsFromName(byte invTypeId, String name) {
|
||||
//player decides from which inventory items should be sold.
|
||||
InventoryType type = InventoryType.getByType(invTypeId);
|
||||
|
||||
Inventory inv = getInventory(type);
|
||||
inv.lockInventory();
|
||||
try {
|
||||
Item it = inv.findByName(name);
|
||||
if (it == null) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (sellAllItemsFromPosition(ii, type, it.getPosition()));
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
}
|
||||
|
||||
public int sellAllItemsFromPosition(ItemInformationProvider ii, InventoryType type, short pos) {
|
||||
int mesoGain = 0;
|
||||
|
||||
Inventory inv = getInventory(type);
|
||||
inv.lockInventory();
|
||||
try {
|
||||
for (short i = pos; i <= inv.getSlotLimit(); i++) {
|
||||
if (inv.getItem(i) == null) {
|
||||
continue;
|
||||
}
|
||||
mesoGain += standaloneSell(getClient(), ii, type, i, inv.getItem(i).getQuantity());
|
||||
}
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
|
||||
return (mesoGain);
|
||||
}
|
||||
|
||||
private int standaloneSell(Client c, ItemInformationProvider ii, InventoryType type, short slot, short quantity) {
|
||||
if (quantity == 0xFFFF || quantity == 0) {
|
||||
quantity = 1;
|
||||
}
|
||||
|
||||
Inventory inv = getInventory(type);
|
||||
inv.lockInventory();
|
||||
try {
|
||||
Item item = inv.getItem(slot);
|
||||
if (item == null) { //Basic check
|
||||
return (0);
|
||||
}
|
||||
|
||||
int itemid = item.getItemId();
|
||||
if (ItemConstants.isRechargeable(itemid)) {
|
||||
quantity = item.getQuantity();
|
||||
} else if (ItemId.isWeddingToken(itemid) || ItemId.isWeddingRing(itemid)) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (quantity < 0) {
|
||||
return (0);
|
||||
}
|
||||
short iQuant = item.getQuantity();
|
||||
if (iQuant == 0xFFFF) {
|
||||
iQuant = 1;
|
||||
}
|
||||
|
||||
if (quantity <= iQuant && iQuant > 0) {
|
||||
InventoryManipulator.removeFromSlot(c, type, (byte) slot, quantity, false);
|
||||
int recvMesos = ii.getPrice(itemid, quantity);
|
||||
if (recvMesos > 0) {
|
||||
gainMeso(recvMesos, false);
|
||||
return (recvMesos);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasMergeFlag(Item item) {
|
||||
return (item.getFlag() & ItemConstants.MERGE_UNTRADEABLE) == ItemConstants.MERGE_UNTRADEABLE;
|
||||
}
|
||||
|
||||
private static void setMergeFlag(Item item) {
|
||||
short flag = item.getFlag();
|
||||
flag |= ItemConstants.MERGE_UNTRADEABLE;
|
||||
flag |= ItemConstants.UNTRADEABLE;
|
||||
item.setFlag(flag);
|
||||
}
|
||||
|
||||
private List<Equip> getUpgradeableEquipped() {
|
||||
List<Equip> list = new LinkedList<>();
|
||||
|
||||
for (Item item : getInventory(InventoryType.EQUIPPED)) {
|
||||
if (ii.isUpgradeable(item.getItemId())) {
|
||||
list.add((Equip) item);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<Equip> getEquipsWithStat(List<Pair<Equip, Map<StatUpgrade, Short>>> equipped, StatUpgrade stat) {
|
||||
List<Equip> equippedWithStat = new LinkedList<>();
|
||||
|
||||
for (Pair<Equip, Map<StatUpgrade, Short>> eq : equipped) {
|
||||
if (eq.getRight().containsKey(stat)) {
|
||||
equippedWithStat.add(eq.getLeft());
|
||||
}
|
||||
}
|
||||
|
||||
return equippedWithStat;
|
||||
}
|
||||
|
||||
public boolean mergeAllItemsFromName(String name) {
|
||||
InventoryType type = InventoryType.EQUIP;
|
||||
|
||||
Inventory inv = getInventory(type);
|
||||
inv.lockInventory();
|
||||
try {
|
||||
Item it = inv.findByName(name);
|
||||
if (it == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<StatUpgrade, Float> statups = new LinkedHashMap<>();
|
||||
mergeAllItemsFromPosition(statups, it.getPosition());
|
||||
|
||||
List<Pair<Equip, Map<StatUpgrade, Short>>> upgradeableEquipped = new LinkedList<>();
|
||||
Map<Equip, List<Pair<StatUpgrade, Integer>>> equipUpgrades = new LinkedHashMap<>();
|
||||
for (Equip eq : getUpgradeableEquipped()) {
|
||||
upgradeableEquipped.add(new Pair<>(eq, eq.getStats()));
|
||||
equipUpgrades.put(eq, new LinkedList<Pair<StatUpgrade, Integer>>());
|
||||
}
|
||||
|
||||
/*
|
||||
for (Entry<StatUpgrade, Float> es : statups.entrySet()) {
|
||||
System.out.println(es);
|
||||
}
|
||||
*/
|
||||
|
||||
for (Entry<StatUpgrade, Float> e : statups.entrySet()) {
|
||||
Double ev = Math.sqrt(e.getValue());
|
||||
|
||||
Set<Equip> extraEquipped = new LinkedHashSet<>(equipUpgrades.keySet());
|
||||
List<Equip> statEquipped = getEquipsWithStat(upgradeableEquipped, e.getKey());
|
||||
float extraRate = (float) (0.2 * Math.random());
|
||||
|
||||
if (!statEquipped.isEmpty()) {
|
||||
float statRate = 1.0f - extraRate;
|
||||
|
||||
int statup = (int) Math.ceil((ev * statRate) / statEquipped.size());
|
||||
for (Equip statEq : statEquipped) {
|
||||
equipUpgrades.get(statEq).add(new Pair<>(e.getKey(), statup));
|
||||
extraEquipped.remove(statEq);
|
||||
}
|
||||
}
|
||||
|
||||
if (!extraEquipped.isEmpty()) {
|
||||
int statup = (int) Math.round((ev * extraRate) / extraEquipped.size());
|
||||
if (statup > 0) {
|
||||
for (Equip extraEq : extraEquipped) {
|
||||
equipUpgrades.get(extraEq).add(new Pair<>(e.getKey(), statup));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropMessage(6, "EQUIPMENT MERGE operation results:");
|
||||
for (Entry<Equip, List<Pair<StatUpgrade, Integer>>> eqpUpg : equipUpgrades.entrySet()) {
|
||||
List<Pair<StatUpgrade, Integer>> eqpStatups = eqpUpg.getValue();
|
||||
if (!eqpStatups.isEmpty()) {
|
||||
Equip eqp = eqpUpg.getKey();
|
||||
setMergeFlag(eqp);
|
||||
|
||||
String showStr = " '" + ItemInformationProvider.getInstance().getName(eqp.getItemId()) + "': ";
|
||||
String upgdStr = eqp.gainStats(eqpStatups).getLeft();
|
||||
|
||||
this.forceUpdateItem(eqp);
|
||||
|
||||
showStr += upgdStr;
|
||||
dropMessage(6, showStr);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
}
|
||||
|
||||
public void mergeAllItemsFromPosition(Map<StatUpgrade, Float> statups, short pos) {
|
||||
Inventory inv = getInventory(InventoryType.EQUIP);
|
||||
inv.lockInventory();
|
||||
try {
|
||||
for (short i = pos; i <= inv.getSlotLimit(); i++) {
|
||||
standaloneMerge(statups, getClient(), InventoryType.EQUIP, i, inv.getItem(i));
|
||||
}
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
}
|
||||
|
||||
private void standaloneMerge(Map<StatUpgrade, Float> statups, Client c, InventoryType type, short slot, Item item) {
|
||||
short quantity;
|
||||
if (item == null || (quantity = item.getQuantity()) < 1 || ii.isCash(item.getItemId()) || !ii.isUpgradeable(item.getItemId()) || hasMergeFlag(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Equip e = (Equip) item;
|
||||
for (Entry<StatUpgrade, Short> s : e.getStats().entrySet()) {
|
||||
Float newVal = statups.get(s.getKey());
|
||||
|
||||
float incVal = s.getValue().floatValue();
|
||||
switch (s.getKey()) {
|
||||
case incPAD:
|
||||
case incMAD:
|
||||
case incPDD:
|
||||
case incMDD:
|
||||
incVal = (float) Math.log(incVal);
|
||||
break;
|
||||
}
|
||||
|
||||
if (newVal != null) {
|
||||
newVal += incVal;
|
||||
} else {
|
||||
newVal = incVal;
|
||||
}
|
||||
|
||||
statups.put(s.getKey(), newVal);
|
||||
}
|
||||
|
||||
InventoryManipulator.removeFromSlot(c, type, (byte) slot, quantity, false);
|
||||
}
|
||||
|
||||
public void setShop(Shop shop) {
|
||||
this.shop = shop;
|
||||
}
|
||||
@@ -9619,7 +9255,7 @@ public class Character extends AbstractCharacterObject {
|
||||
String medal = "";
|
||||
Item medalItem = mapOwner.getInventory(InventoryType.EQUIPPED).getItem((short) -49);
|
||||
if (medalItem != null) {
|
||||
medal = "<" + ii.getName(medalItem.getItemId()) + "> ";
|
||||
medal = "<" + ItemInformationProvider.getInstance().getName(medalItem.getItemId()) + "> ";
|
||||
}
|
||||
|
||||
List<String> strLines = new LinkedList<>();
|
||||
@@ -9640,21 +9276,6 @@ public class Character extends AbstractCharacterObject {
|
||||
client.announceHint(msg, length);
|
||||
}
|
||||
|
||||
public void showNote() {
|
||||
try (Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT * FROM notes WHERE `to` = ? AND `deleted` = 0", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) {
|
||||
ps.setString(1, this.getName());
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
rs.last();
|
||||
int count = rs.getRow();
|
||||
rs.first();
|
||||
sendPacket(PacketCreator.showNotes(rs, count));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void silentGiveBuffs(List<Pair<Long, PlayerBuffValueHolder>> buffs) {
|
||||
for (Pair<Long, PlayerBuffValueHolder> mbsv : buffs) {
|
||||
PlayerBuffValueHolder mbsvh = mbsv.getRight();
|
||||
@@ -10357,6 +9978,7 @@ public class Character extends AbstractCharacterObject {
|
||||
}
|
||||
|
||||
Collection<Item> eqpList = new LinkedHashSet<>();
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
for (Item it : fullList) {
|
||||
if (!ii.isCash(it.getItemId())) {
|
||||
eqpList.add(it);
|
||||
@@ -10372,6 +9994,7 @@ public class Character extends AbstractCharacterObject {
|
||||
expGain = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
for (Item item : getUpgradeableEquipList()) {
|
||||
Equip nEquip = (Equip) item;
|
||||
String itemName = ii.getName(nEquip.getItemId());
|
||||
@@ -10387,6 +10010,7 @@ public class Character extends AbstractCharacterObject {
|
||||
public void showAllEquipFeatures() {
|
||||
String showMsg = "";
|
||||
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
for (Item item : getInventory(InventoryType.EQUIPPED).list()) {
|
||||
Equip nEquip = (Equip) item;
|
||||
String itemName = ii.getName(nEquip.getItemId());
|
||||
@@ -11097,70 +10721,6 @@ public class Character extends AbstractCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public void setReborns(int value) {
|
||||
if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
|
||||
yellowMessage("Rebirth system is not enabled!");
|
||||
throw new NotEnabledException();
|
||||
}
|
||||
|
||||
try (Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("UPDATE characters SET reborns=? WHERE id=?;")) {
|
||||
ps.setInt(1, value);
|
||||
ps.setInt(2, id);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void addReborns() {
|
||||
setReborns(getReborns() + 1);
|
||||
}
|
||||
|
||||
public int getReborns() {
|
||||
if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
|
||||
yellowMessage("Rebirth system is not enabled!");
|
||||
throw new NotEnabledException();
|
||||
}
|
||||
|
||||
try (Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT reborns FROM characters WHERE id=?;")) {
|
||||
ps.setInt(1, id);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
rs.next();
|
||||
return rs.getInt(1);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
public void executeReborn() {
|
||||
// default to beginner: job id = 0
|
||||
// this prevents a breaking change
|
||||
executeRebornAs(Job.BEGINNER);
|
||||
}
|
||||
|
||||
public void executeRebornAsId(int jobId) {
|
||||
executeRebornAs(Job.getById(jobId));
|
||||
}
|
||||
|
||||
public void executeRebornAs(Job job) {
|
||||
if (!YamlConfig.config.server.USE_REBIRTH_SYSTEM) {
|
||||
yellowMessage("Rebirth system is not enabled!");
|
||||
throw new NotEnabledException();
|
||||
}
|
||||
if (getLevel() != getMaxClassLevel()) {
|
||||
return;
|
||||
}
|
||||
addReborns();
|
||||
changeJob(job);
|
||||
setLevel(0);
|
||||
levelUp(true);
|
||||
}
|
||||
|
||||
//EVENTS
|
||||
private byte team = 0;
|
||||
private Fitness fitness;
|
||||
|
||||
@@ -39,13 +39,16 @@ import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
import net.server.coordinator.login.LoginBypassCoordinator;
|
||||
import net.server.coordinator.session.Hwid;
|
||||
import net.server.coordinator.session.IpAddresses;
|
||||
import net.server.coordinator.session.SessionCoordinator;
|
||||
import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult;
|
||||
import net.server.guild.Guild;
|
||||
import net.server.guild.GuildCharacter;
|
||||
import net.server.guild.GuildPackets;
|
||||
import net.server.world.*;
|
||||
import net.server.world.MessengerCharacter;
|
||||
import net.server.world.Party;
|
||||
import net.server.world.PartyCharacter;
|
||||
import net.server.world.PartyOperation;
|
||||
import net.server.world.World;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import scripting.AbstractPlayerInteraction;
|
||||
@@ -74,9 +77,23 @@ import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.sql.*;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -176,10 +193,7 @@ public class Client extends ChannelInboundHandlerAdapter {
|
||||
private static String getRemoteAddress(io.netty.channel.Channel channel) {
|
||||
String remoteAddress = "null";
|
||||
try {
|
||||
String hostAddress = ((InetSocketAddress) channel.remoteAddress()).getAddress().getHostAddress();
|
||||
if (hostAddress != null) {
|
||||
remoteAddress = IpAddresses.evaluateRemoteAddress(hostAddress); // thanks dyz for noticing Local/LAN/WAN connections not interacting properly
|
||||
}
|
||||
remoteAddress = ((InetSocketAddress) channel.remoteAddress()).getAddress().getHostAddress();
|
||||
} catch (NullPointerException npe) {
|
||||
log.warn("Unable to get remote address for client", npe);
|
||||
}
|
||||
@@ -1504,7 +1518,6 @@ public class Client extends ChannelInboundHandlerAdapter {
|
||||
|
||||
player.getInventory(InventoryType.EQUIPPED).checked(false); //test
|
||||
player.getMap().removePlayer(player);
|
||||
player.clearBanishPlayerData();
|
||||
player.getClient().getChannelServer().removePlayer(player);
|
||||
|
||||
player.saveCharToDB();
|
||||
@@ -1585,4 +1598,4 @@ public class Client extends ChannelInboundHandlerAdapter {
|
||||
public void setLanguage(int lingua) {
|
||||
this.lang = lingua;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,12 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
|
||||
@@ -24,7 +24,11 @@ package client;
|
||||
import server.quest.Quest;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Matze
|
||||
|
||||
@@ -21,8 +21,63 @@
|
||||
*/
|
||||
package client;
|
||||
|
||||
import constants.skills.*;
|
||||
import provider.*;
|
||||
import constants.skills.Aran;
|
||||
import constants.skills.Archer;
|
||||
import constants.skills.Assassin;
|
||||
import constants.skills.Bandit;
|
||||
import constants.skills.Beginner;
|
||||
import constants.skills.Bishop;
|
||||
import constants.skills.BlazeWizard;
|
||||
import constants.skills.Bowmaster;
|
||||
import constants.skills.Buccaneer;
|
||||
import constants.skills.ChiefBandit;
|
||||
import constants.skills.Cleric;
|
||||
import constants.skills.Corsair;
|
||||
import constants.skills.Crossbowman;
|
||||
import constants.skills.Crusader;
|
||||
import constants.skills.DarkKnight;
|
||||
import constants.skills.DawnWarrior;
|
||||
import constants.skills.DragonKnight;
|
||||
import constants.skills.Evan;
|
||||
import constants.skills.FPArchMage;
|
||||
import constants.skills.FPMage;
|
||||
import constants.skills.FPWizard;
|
||||
import constants.skills.Fighter;
|
||||
import constants.skills.GM;
|
||||
import constants.skills.Gunslinger;
|
||||
import constants.skills.Hermit;
|
||||
import constants.skills.Hero;
|
||||
import constants.skills.Hunter;
|
||||
import constants.skills.ILArchMage;
|
||||
import constants.skills.ILMage;
|
||||
import constants.skills.ILWizard;
|
||||
import constants.skills.Legend;
|
||||
import constants.skills.Magician;
|
||||
import constants.skills.Marauder;
|
||||
import constants.skills.Marksman;
|
||||
import constants.skills.NightLord;
|
||||
import constants.skills.NightWalker;
|
||||
import constants.skills.Noblesse;
|
||||
import constants.skills.Page;
|
||||
import constants.skills.Paladin;
|
||||
import constants.skills.Pirate;
|
||||
import constants.skills.Priest;
|
||||
import constants.skills.Ranger;
|
||||
import constants.skills.Rogue;
|
||||
import constants.skills.Shadower;
|
||||
import constants.skills.Sniper;
|
||||
import constants.skills.Spearman;
|
||||
import constants.skills.SuperGM;
|
||||
import constants.skills.ThunderBreaker;
|
||||
import constants.skills.Warrior;
|
||||
import constants.skills.WhiteKnight;
|
||||
import constants.skills.WindArcher;
|
||||
import provider.Data;
|
||||
import provider.DataDirectoryEntry;
|
||||
import provider.DataFileEntry;
|
||||
import provider.DataProvider;
|
||||
import provider.DataProviderFactory;
|
||||
import provider.DataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import server.StatEffect;
|
||||
import server.life.Element;
|
||||
|
||||
@@ -22,14 +22,15 @@
|
||||
package client;
|
||||
|
||||
public enum SkinColor {
|
||||
NORMAL(0),
|
||||
DARK(1),
|
||||
BLACK(2),
|
||||
LIGHT(0),
|
||||
TANNED(1),
|
||||
DARK(2),
|
||||
PALE(3),
|
||||
BLUE(4),
|
||||
GREEN(5),
|
||||
WHITE(9),
|
||||
PINK(10);
|
||||
PINK(10),
|
||||
BROWN(11);
|
||||
|
||||
final int id;
|
||||
|
||||
|
||||
@@ -24,13 +24,180 @@
|
||||
package client.command;
|
||||
|
||||
import client.Client;
|
||||
import client.command.commands.gm0.*;
|
||||
import client.command.commands.gm1.*;
|
||||
import client.command.commands.gm2.*;
|
||||
import client.command.commands.gm3.*;
|
||||
import client.command.commands.gm4.*;
|
||||
import client.command.commands.gm5.*;
|
||||
import client.command.commands.gm6.*;
|
||||
import client.command.commands.gm0.ChangeLanguageCommand;
|
||||
import client.command.commands.gm0.DisposeCommand;
|
||||
import client.command.commands.gm0.DropLimitCommand;
|
||||
import client.command.commands.gm0.EnableAuthCommand;
|
||||
import client.command.commands.gm0.EquipLvCommand;
|
||||
import client.command.commands.gm0.GachaCommand;
|
||||
import client.command.commands.gm0.GmCommand;
|
||||
import client.command.commands.gm0.HelpCommand;
|
||||
import client.command.commands.gm0.JoinEventCommand;
|
||||
import client.command.commands.gm0.LeaveEventCommand;
|
||||
import client.command.commands.gm0.MapOwnerClaimCommand;
|
||||
import client.command.commands.gm0.OnlineCommand;
|
||||
import client.command.commands.gm0.RanksCommand;
|
||||
import client.command.commands.gm0.RatesCommand;
|
||||
import client.command.commands.gm0.ReadPointsCommand;
|
||||
import client.command.commands.gm0.ReportBugCommand;
|
||||
import client.command.commands.gm0.ShowRatesCommand;
|
||||
import client.command.commands.gm0.StaffCommand;
|
||||
import client.command.commands.gm0.StatDexCommand;
|
||||
import client.command.commands.gm0.StatIntCommand;
|
||||
import client.command.commands.gm0.StatLukCommand;
|
||||
import client.command.commands.gm0.StatStrCommand;
|
||||
import client.command.commands.gm0.TimeCommand;
|
||||
import client.command.commands.gm0.ToggleExpCommand;
|
||||
import client.command.commands.gm0.UptimeCommand;
|
||||
import client.command.commands.gm1.BossHpCommand;
|
||||
import client.command.commands.gm1.BuffMeCommand;
|
||||
import client.command.commands.gm1.GotoCommand;
|
||||
import client.command.commands.gm1.MobHpCommand;
|
||||
import client.command.commands.gm1.WhatDropsFromCommand;
|
||||
import client.command.commands.gm1.WhoDropsCommand;
|
||||
import client.command.commands.gm2.ApCommand;
|
||||
import client.command.commands.gm2.BombCommand;
|
||||
import client.command.commands.gm2.BuffCommand;
|
||||
import client.command.commands.gm2.BuffMapCommand;
|
||||
import client.command.commands.gm2.ClearDropsCommand;
|
||||
import client.command.commands.gm2.ClearSavedLocationsCommand;
|
||||
import client.command.commands.gm2.ClearSlotCommand;
|
||||
import client.command.commands.gm2.DcCommand;
|
||||
import client.command.commands.gm2.EmpowerMeCommand;
|
||||
import client.command.commands.gm2.GachaListCommand;
|
||||
import client.command.commands.gm2.GmShopCommand;
|
||||
import client.command.commands.gm2.HealCommand;
|
||||
import client.command.commands.gm2.HideCommand;
|
||||
import client.command.commands.gm2.IdCommand;
|
||||
import client.command.commands.gm2.ItemCommand;
|
||||
import client.command.commands.gm2.ItemDropCommand;
|
||||
import client.command.commands.gm2.JailCommand;
|
||||
import client.command.commands.gm2.JobCommand;
|
||||
import client.command.commands.gm2.LevelCommand;
|
||||
import client.command.commands.gm2.LevelProCommand;
|
||||
import client.command.commands.gm2.LootCommand;
|
||||
import client.command.commands.gm2.MaxSkillCommand;
|
||||
import client.command.commands.gm2.MaxStatCommand;
|
||||
import client.command.commands.gm2.MobSkillCommand;
|
||||
import client.command.commands.gm2.ReachCommand;
|
||||
import client.command.commands.gm2.RechargeCommand;
|
||||
import client.command.commands.gm2.ResetSkillCommand;
|
||||
import client.command.commands.gm2.SearchCommand;
|
||||
import client.command.commands.gm2.SetSlotCommand;
|
||||
import client.command.commands.gm2.SetStatCommand;
|
||||
import client.command.commands.gm2.SpCommand;
|
||||
import client.command.commands.gm2.SummonCommand;
|
||||
import client.command.commands.gm2.UnBugCommand;
|
||||
import client.command.commands.gm2.UnHideCommand;
|
||||
import client.command.commands.gm2.UnJailCommand;
|
||||
import client.command.commands.gm2.WarpAreaCommand;
|
||||
import client.command.commands.gm2.WarpCommand;
|
||||
import client.command.commands.gm2.WarpMapCommand;
|
||||
import client.command.commands.gm2.WhereaMiCommand;
|
||||
import client.command.commands.gm3.BanCommand;
|
||||
import client.command.commands.gm3.ChatCommand;
|
||||
import client.command.commands.gm3.CheckDmgCommand;
|
||||
import client.command.commands.gm3.ClosePortalCommand;
|
||||
import client.command.commands.gm3.DebuffCommand;
|
||||
import client.command.commands.gm3.EndEventCommand;
|
||||
import client.command.commands.gm3.ExpedsCommand;
|
||||
import client.command.commands.gm3.FaceCommand;
|
||||
import client.command.commands.gm3.FameCommand;
|
||||
import client.command.commands.gm3.FlyCommand;
|
||||
import client.command.commands.gm3.GiveMesosCommand;
|
||||
import client.command.commands.gm3.GiveNxCommand;
|
||||
import client.command.commands.gm3.GiveRpCommand;
|
||||
import client.command.commands.gm3.GiveVpCommand;
|
||||
import client.command.commands.gm3.HairCommand;
|
||||
import client.command.commands.gm3.HealMapCommand;
|
||||
import client.command.commands.gm3.HealPersonCommand;
|
||||
import client.command.commands.gm3.HpMpCommand;
|
||||
import client.command.commands.gm3.HurtCommand;
|
||||
import client.command.commands.gm3.IgnoreCommand;
|
||||
import client.command.commands.gm3.IgnoredCommand;
|
||||
import client.command.commands.gm3.InMapCommand;
|
||||
import client.command.commands.gm3.KillAllCommand;
|
||||
import client.command.commands.gm3.KillCommand;
|
||||
import client.command.commands.gm3.KillMapCommand;
|
||||
import client.command.commands.gm3.MaxEnergyCommand;
|
||||
import client.command.commands.gm3.MaxHpMpCommand;
|
||||
import client.command.commands.gm3.MonitorCommand;
|
||||
import client.command.commands.gm3.MonitorsCommand;
|
||||
import client.command.commands.gm3.MusicCommand;
|
||||
import client.command.commands.gm3.MuteMapCommand;
|
||||
import client.command.commands.gm3.NightCommand;
|
||||
import client.command.commands.gm3.NoticeCommand;
|
||||
import client.command.commands.gm3.NpcCommand;
|
||||
import client.command.commands.gm3.OnlineTwoCommand;
|
||||
import client.command.commands.gm3.OpenPortalCommand;
|
||||
import client.command.commands.gm3.PeCommand;
|
||||
import client.command.commands.gm3.PosCommand;
|
||||
import client.command.commands.gm3.QuestCompleteCommand;
|
||||
import client.command.commands.gm3.QuestResetCommand;
|
||||
import client.command.commands.gm3.QuestStartCommand;
|
||||
import client.command.commands.gm3.ReloadDropsCommand;
|
||||
import client.command.commands.gm3.ReloadEventsCommand;
|
||||
import client.command.commands.gm3.ReloadMapCommand;
|
||||
import client.command.commands.gm3.ReloadPortalsCommand;
|
||||
import client.command.commands.gm3.ReloadShopsCommand;
|
||||
import client.command.commands.gm3.RipCommand;
|
||||
import client.command.commands.gm3.SeedCommand;
|
||||
import client.command.commands.gm3.SpawnCommand;
|
||||
import client.command.commands.gm3.StartEventCommand;
|
||||
import client.command.commands.gm3.StartMapEventCommand;
|
||||
import client.command.commands.gm3.StopMapEventCommand;
|
||||
import client.command.commands.gm3.TimerAllCommand;
|
||||
import client.command.commands.gm3.TimerCommand;
|
||||
import client.command.commands.gm3.TimerMapCommand;
|
||||
import client.command.commands.gm3.ToggleCouponCommand;
|
||||
import client.command.commands.gm3.UnBanCommand;
|
||||
import client.command.commands.gm4.BossDropRateCommand;
|
||||
import client.command.commands.gm4.CakeCommand;
|
||||
import client.command.commands.gm4.DropRateCommand;
|
||||
import client.command.commands.gm4.ExpRateCommand;
|
||||
import client.command.commands.gm4.FishingRateCommand;
|
||||
import client.command.commands.gm4.ForceVacCommand;
|
||||
import client.command.commands.gm4.HorntailCommand;
|
||||
import client.command.commands.gm4.ItemVacCommand;
|
||||
import client.command.commands.gm4.MesoRateCommand;
|
||||
import client.command.commands.gm4.PapCommand;
|
||||
import client.command.commands.gm4.PianusCommand;
|
||||
import client.command.commands.gm4.PinkbeanCommand;
|
||||
import client.command.commands.gm4.PlayerNpcCommand;
|
||||
import client.command.commands.gm4.PlayerNpcRemoveCommand;
|
||||
import client.command.commands.gm4.PmobCommand;
|
||||
import client.command.commands.gm4.PmobRemoveCommand;
|
||||
import client.command.commands.gm4.PnpcCommand;
|
||||
import client.command.commands.gm4.PnpcRemoveCommand;
|
||||
import client.command.commands.gm4.ProItemCommand;
|
||||
import client.command.commands.gm4.QuestRateCommand;
|
||||
import client.command.commands.gm4.ServerMessageCommand;
|
||||
import client.command.commands.gm4.SetEqStatCommand;
|
||||
import client.command.commands.gm4.TravelRateCommand;
|
||||
import client.command.commands.gm4.ZakumCommand;
|
||||
import client.command.commands.gm5.DebugCommand;
|
||||
import client.command.commands.gm5.IpListCommand;
|
||||
import client.command.commands.gm5.SetCommand;
|
||||
import client.command.commands.gm5.ShowMoveLifeCommand;
|
||||
import client.command.commands.gm5.ShowPacketsCommand;
|
||||
import client.command.commands.gm5.ShowSessionsCommand;
|
||||
import client.command.commands.gm6.ClearQuestCacheCommand;
|
||||
import client.command.commands.gm6.ClearQuestCommand;
|
||||
import client.command.commands.gm6.DCAllCommand;
|
||||
import client.command.commands.gm6.DevtestCommand;
|
||||
import client.command.commands.gm6.EraseAllPNpcsCommand;
|
||||
import client.command.commands.gm6.GetAccCommand;
|
||||
import client.command.commands.gm6.MapPlayersCommand;
|
||||
import client.command.commands.gm6.SaveAllCommand;
|
||||
import client.command.commands.gm6.ServerAddChannelCommand;
|
||||
import client.command.commands.gm6.ServerAddWorldCommand;
|
||||
import client.command.commands.gm6.ServerRemoveChannelCommand;
|
||||
import client.command.commands.gm6.ServerRemoveWorldCommand;
|
||||
import client.command.commands.gm6.SetGmLevelCommand;
|
||||
import client.command.commands.gm6.ShutdownCommand;
|
||||
import client.command.commands.gm6.SpawnAllPNpcsCommand;
|
||||
import client.command.commands.gm6.SupplyRateCouponCommand;
|
||||
import client.command.commands.gm6.WarpWorldCommand;
|
||||
import constants.id.MapId;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -177,7 +344,6 @@ public class CommandsExecutor {
|
||||
addCommand("droplimit", DropLimitCommand.class);
|
||||
addCommand("time", TimeCommand.class);
|
||||
addCommand("credits", StaffCommand.class);
|
||||
addCommand("buyback", BuyBackCommand.class);
|
||||
addCommand("uptime", UptimeCommand.class);
|
||||
addCommand("gacha", GachaCommand.class);
|
||||
addCommand("dispose", DisposeCommand.class);
|
||||
@@ -391,6 +557,7 @@ public class CommandsExecutor {
|
||||
addCommand("addworld", 6, ServerAddWorldCommand.class);
|
||||
addCommand("removechannel", 6, ServerRemoveChannelCommand.class);
|
||||
addCommand("removeworld", 6, ServerRemoveWorldCommand.class);
|
||||
addCommand("devtest", 6, DevtestCommand.class);
|
||||
|
||||
commandsNameDesc.add(levelCommandsCursor);
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
@Author: Arthur L - Refactored command content into modules
|
||||
*/
|
||||
package client.command.commands.gm0;
|
||||
|
||||
import client.Client;
|
||||
import client.command.Command;
|
||||
import client.processor.action.BuybackProcessor;
|
||||
|
||||
public class BuyBackCommand extends Command {
|
||||
{
|
||||
setDescription("Revive yourself after a death.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Client c, String[] params) {
|
||||
if (params.length < 1) {
|
||||
c.getPlayer().yellowMessage("Syntax: @buyback <info|now>");
|
||||
return;
|
||||
}
|
||||
|
||||
if (params[0].contentEquals("now")) {
|
||||
BuybackProcessor.processBuyback(c);
|
||||
} else {
|
||||
c.getPlayer().showBuybackInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,10 @@ import client.Client;
|
||||
import client.command.Command;
|
||||
import net.server.Server;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.*;
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
public class UptimeCommand extends Command {
|
||||
{
|
||||
|
||||
@@ -28,7 +28,11 @@ import client.Client;
|
||||
import client.command.Command;
|
||||
import constants.game.GameConstants;
|
||||
import constants.id.NpcId;
|
||||
import server.maps.*;
|
||||
import server.maps.FieldLimit;
|
||||
import server.maps.MapFactory;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MiniDungeonInfo;
|
||||
import server.maps.Portal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -3,68 +3,125 @@ package client.command.commands.gm2;
|
||||
import client.Character;
|
||||
import client.Client;
|
||||
import client.command.Command;
|
||||
import constants.game.NpcChat;
|
||||
import constants.id.NpcId;
|
||||
import server.ThreadManager;
|
||||
import tools.exceptions.IdTypeNotSupportedException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class IdCommand extends Command {
|
||||
{
|
||||
setDescription("Search in handbook.");
|
||||
}
|
||||
private final static int MAX_SEARCH_HITS = 100;
|
||||
private final Map<String, String> handbookDirectory = typeFilePaths();
|
||||
private final Map<String, HandbookFileItems> typeItems = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, String> handbookDirectory = new HashMap<>();
|
||||
private final Map<String, HashMap<String, String>> itemMap = new HashMap<>();
|
||||
private Map<String, String> typeFilePaths() {
|
||||
return Map.ofEntries(
|
||||
Map.entry("map", "handbook/Map.txt"),
|
||||
Map.entry("etc", "handbook/Etc.txt"),
|
||||
Map.entry("npc", "handbook/NPC.txt"),
|
||||
Map.entry("use", "handbook/Use.txt"),
|
||||
Map.entry("weapon", "handbook/Equip/Weapon.txt") // TODO add more into this
|
||||
);
|
||||
}
|
||||
|
||||
public IdCommand() {
|
||||
handbookDirectory.put("map", "handbook/Map.txt");
|
||||
handbookDirectory.put("etc", "handbook/Etc.txt");
|
||||
handbookDirectory.put("npc", "handbook/NPC.txt");
|
||||
handbookDirectory.put("use", "handbook/Use.txt");
|
||||
handbookDirectory.put("weapon", "handbook/Equip/Weapon.txt"); // TODO add more into this
|
||||
private static class HandbookFileItems {
|
||||
private final List<HandbookItem> items;
|
||||
|
||||
public HandbookFileItems(List<String> fileLines) {
|
||||
this.items = fileLines.stream()
|
||||
.map(this::parseLine)
|
||||
.filter(Predicate.not(Objects::isNull))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private HandbookItem parseLine(String line) {
|
||||
if (line == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] splitLine = line.split(" - ", 2);
|
||||
if (splitLine.length < 2 || splitLine[1].isBlank()) {
|
||||
return null;
|
||||
}
|
||||
return new HandbookItem(splitLine[0], splitLine[1]);
|
||||
}
|
||||
|
||||
public List<HandbookItem> search(String query) {
|
||||
if (query == null || query.isBlank()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return items.stream()
|
||||
.filter(item -> item.matches(query))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
private record HandbookItem(String id, String name) {
|
||||
|
||||
public HandbookItem {
|
||||
Objects.requireNonNull(id);
|
||||
Objects.requireNonNull(name);
|
||||
}
|
||||
|
||||
public boolean matches(String query) {
|
||||
if (query == null) {
|
||||
return false;
|
||||
}
|
||||
return this.name.toLowerCase().contains(query.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Client client, final String[] params) {
|
||||
final Character player = client.getPlayer();
|
||||
final Character chr = client.getPlayer();
|
||||
if (params.length < 2) {
|
||||
player.yellowMessage("Syntax: !id <type> <query>");
|
||||
chr.yellowMessage("Syntax: !id <type> <query>");
|
||||
return;
|
||||
}
|
||||
final String queryItem = joinStringArr(Arrays.copyOfRange(params, 1, params.length), " ");
|
||||
player.yellowMessage("Querying for entry... May take some time... Please try to refine your search.");
|
||||
final String type = params[0].toLowerCase();
|
||||
final String[] queryItems = Arrays.copyOfRange(params, 1, params.length);
|
||||
final String query = String.join(" ", queryItems);
|
||||
chr.yellowMessage("Querying for entry... May take some time... Please try to refine your search.");
|
||||
Runnable queryRunnable = () -> {
|
||||
try {
|
||||
populateIdMap(params[0].toLowerCase());
|
||||
populateIdMap(type);
|
||||
|
||||
Map<String, String> resultList = fetchResults(itemMap.get(params[0]), queryItem);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
final HandbookFileItems handbookFileItems = typeItems.get(type);
|
||||
if (handbookFileItems == null) {
|
||||
return;
|
||||
}
|
||||
final List<HandbookItem> searchHits = handbookFileItems.search(query);
|
||||
|
||||
if (resultList.size() > 0) {
|
||||
int count = 0;
|
||||
for (Map.Entry<String, String> entry : resultList.entrySet()) {
|
||||
sb.append(String.format("Id for %s is: #b%s#k", entry.getKey(), entry.getValue()) + "\r\n");
|
||||
if (++count > 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
sb.append(String.format("Results found: #r%d#k | Returned: #b%d#k/100 | Refine search query to improve time.", resultList.size(), count) + "\r\n");
|
||||
|
||||
player.getAbstractPlayerInteraction().npcTalk(NpcId.MAPLE_ADMINISTRATOR, sb.toString());
|
||||
if (!searchHits.isEmpty()) {
|
||||
String searchHitsText = searchHits.stream()
|
||||
.limit(MAX_SEARCH_HITS)
|
||||
.map(item -> "Id for %s is: #b%s#k".formatted(item.name, item.id))
|
||||
.collect(Collectors.joining(NpcChat.NEW_LINE));
|
||||
int hitsCount = Math.min(searchHits.size(), MAX_SEARCH_HITS);
|
||||
String summaryText = "Results found: #r%d#k | Returned: #b%d#k/100 | Refine search query to improve time.".formatted(searchHits.size(), hitsCount);
|
||||
String fullText = searchHitsText + NpcChat.NEW_LINE + summaryText;
|
||||
chr.getAbstractPlayerInteraction().npcTalk(NpcId.MAPLE_ADMINISTRATOR, fullText);
|
||||
} else {
|
||||
player.yellowMessage(String.format("Id not found for item: %s, of type: %s.", queryItem, params[0]));
|
||||
chr.yellowMessage(String.format("Id not found for item: %s, of type: %s.", query, type));
|
||||
}
|
||||
} catch (IdTypeNotSupportedException e) {
|
||||
player.yellowMessage("Your query type is not supported.");
|
||||
chr.yellowMessage("Your query type is not supported.");
|
||||
} catch (IOException e) {
|
||||
player.yellowMessage("Error reading file, please contact your administrator.");
|
||||
chr.yellowMessage("Error reading file, please contact your administrator.");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -72,40 +129,15 @@ public class IdCommand extends Command {
|
||||
}
|
||||
|
||||
private void populateIdMap(String type) throws IdTypeNotSupportedException, IOException {
|
||||
if (!handbookDirectory.containsKey(type)) {
|
||||
final String filePath = handbookDirectory.get(type);
|
||||
if (filePath == null) {
|
||||
throw new IdTypeNotSupportedException();
|
||||
}
|
||||
itemMap.put(type, new HashMap<>());
|
||||
try (BufferedReader reader = Files.newBufferedReader(Path.of(handbookDirectory.get(type)))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
String[] row = line.split(" - ", 2);
|
||||
if (row.length == 2) {
|
||||
itemMap.get(type).put(row[1].toLowerCase(), row[0]);
|
||||
}
|
||||
}
|
||||
if (typeItems.containsKey(type)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private String joinStringArr(String[] arr, String separator) {
|
||||
if (null == arr || 0 == arr.length) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(256);
|
||||
sb.append(arr[0]);
|
||||
for (int i = 1; i < arr.length; i++) {
|
||||
sb.append(separator).append(arr[i]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private Map<String, String> fetchResults(Map<String, String> queryMap, String queryItem) {
|
||||
Map<String, String> results = new HashMap<>();
|
||||
for (String item : queryMap.keySet()) {
|
||||
if (item.indexOf(queryItem) != -1) {
|
||||
results.put(item, queryMap.get(item));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
final List<String> fileLines = Files.readAllLines(Path.of(filePath));
|
||||
typeItems.put(type, new HandbookFileItems(fileLines));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
package client.command.commands.gm2;
|
||||
|
||||
import client.Character;
|
||||
import client.*;
|
||||
import client.Client;
|
||||
import client.Job;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.command.Command;
|
||||
import provider.Data;
|
||||
import provider.DataProviderFactory;
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
package client.command.commands.gm2;
|
||||
|
||||
import client.Character;
|
||||
import client.*;
|
||||
import client.Client;
|
||||
import client.Job;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.command.Command;
|
||||
import provider.Data;
|
||||
import provider.DataProviderFactory;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package client.command.commands.gm6;
|
||||
|
||||
import client.Client;
|
||||
import client.command.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import scripting.AbstractScriptManager;
|
||||
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
public class DevtestCommand extends Command {
|
||||
{
|
||||
setDescription("Runs devtest.js. Developer utility - test stuff without restarting the server.");
|
||||
}
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DevtestCommand.class);
|
||||
|
||||
private static class DevtestScriptManager extends AbstractScriptManager {
|
||||
|
||||
@Override
|
||||
public ScriptEngine getInvocableScriptEngine(String path) {
|
||||
return super.getInvocableScriptEngine(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Client client, String[] params) {
|
||||
DevtestScriptManager scriptManager = new DevtestScriptManager();
|
||||
ScriptEngine scriptEngine = scriptManager.getInvocableScriptEngine("devtest.js");
|
||||
try {
|
||||
Invocable invocable = (Invocable) scriptEngine;
|
||||
invocable.invokeFunction("run", client.getPlayer());
|
||||
} catch (ScriptException | NoSuchMethodException e) {
|
||||
log.info("devtest.js run() threw an exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,10 @@ import net.server.Server;
|
||||
import net.server.world.World;
|
||||
import server.TimerManager;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.*;
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
public class ShutdownCommand extends Command {
|
||||
{
|
||||
|
||||
@@ -47,19 +47,19 @@ public abstract class CharacterFactory {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Character newchar = Character.getDefault(c);
|
||||
newchar.setWorld(c.getWorld());
|
||||
newchar.setSkinColor(SkinColor.getById(skin));
|
||||
newchar.setGender(gender);
|
||||
newchar.setName(name);
|
||||
newchar.setHair(hair);
|
||||
newchar.setFace(face);
|
||||
Character newCharacter = Character.getDefault(c);
|
||||
newCharacter.setWorld(c.getWorld());
|
||||
newCharacter.setSkinColor(SkinColor.getById(skin));
|
||||
newCharacter.setGender(gender);
|
||||
newCharacter.setName(name);
|
||||
newCharacter.setHair(hair);
|
||||
newCharacter.setFace(face);
|
||||
|
||||
newchar.setLevel(recipe.getLevel());
|
||||
newchar.setJob(recipe.getJob());
|
||||
newchar.setMapId(recipe.getMap());
|
||||
newCharacter.setLevel(recipe.getLevel());
|
||||
newCharacter.setJob(recipe.getJob());
|
||||
newCharacter.setMapId(recipe.getMap());
|
||||
|
||||
Inventory equipped = newchar.getInventory(InventoryType.EQUIPPED);
|
||||
Inventory equipped = newCharacter.getInventory(InventoryType.EQUIPPED);
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
|
||||
int top = recipe.getTop(), bottom = recipe.getBottom(), shoes = recipe.getShoes(), weapon = recipe.getWeapon();
|
||||
@@ -88,12 +88,17 @@ public abstract class CharacterFactory {
|
||||
equipped.addItemFromDB(eq_weapon.copy());
|
||||
}
|
||||
|
||||
if (!newchar.insertNewChar(recipe)) {
|
||||
if (!MakeCharInfoValidator.isNewCharacterValid(newCharacter)) {
|
||||
log.warn("Owner from account {} tried to packet edit in character creation", c.getAccountName());
|
||||
return -2;
|
||||
}
|
||||
c.sendPacket(PacketCreator.addNewCharEntry(newchar));
|
||||
|
||||
Server.getInstance().createCharacterEntry(newchar);
|
||||
if (!newCharacter.insertNewChar(recipe)) {
|
||||
return -2;
|
||||
}
|
||||
c.sendPacket(PacketCreator.addNewCharEntry(newCharacter));
|
||||
|
||||
Server.getInstance().createCharacterEntry(newCharacter);
|
||||
Server.getInstance().broadcastGMMessage(c.getWorld(), PacketCreator.sendYellowTip("[New Char]: " + c.getAccountName() + " has created a new character with IGN " + name));
|
||||
log.info("Account {} created chr with name {}", c.getAccountName(), name);
|
||||
|
||||
|
||||
140
src/main/java/client/creator/MakeCharInfo.java
Normal file
140
src/main/java/client/creator/MakeCharInfo.java
Normal file
@@ -0,0 +1,140 @@
|
||||
package client.creator;
|
||||
|
||||
import client.Character;
|
||||
import client.Job;
|
||||
import client.inventory.InventoryType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import provider.Data;
|
||||
import provider.DataTool;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class MakeCharInfo {
|
||||
private static final Logger log = LoggerFactory.getLogger(MakeCharInfo.class);
|
||||
private static final String FACE_ID = "0";
|
||||
private static final String HAIR_ID = "1";
|
||||
private static final String HAIR_COLOR_ID = "2";
|
||||
private static final String SKIN_ID = "3";
|
||||
private static final String TOP_ID = "4";
|
||||
private static final String BOTTOM_ID = "5";
|
||||
private static final String SHOE_ID = "6";
|
||||
private static final String WEAPON_ID = "7";
|
||||
|
||||
private final Set<Integer> charFaces = new HashSet<>();
|
||||
private final Set<Integer> charHairs = new HashSet<>();
|
||||
private final Set<Integer> charHairColors = new HashSet<>();
|
||||
private final Set<Integer> charSkins = new HashSet<>();
|
||||
private final Set<Integer> charTops = new HashSet<>();
|
||||
private final Set<Integer> charBottoms = new HashSet<>();
|
||||
private final Set<Integer> charShoes = new HashSet<>();
|
||||
private final Set<Integer> charWeapons = new HashSet<>();
|
||||
|
||||
public MakeCharInfo(Data charInfoData) {
|
||||
for (Data data : charInfoData.getChildren()) {
|
||||
switch (data.getName()) {
|
||||
case FACE_ID -> {
|
||||
for (Data faceData : data) {
|
||||
charFaces.add(DataTool.getInt(faceData));
|
||||
}
|
||||
}
|
||||
case HAIR_ID -> {
|
||||
for (Data hairData : data) {
|
||||
charHairs.add(DataTool.getInt(hairData));
|
||||
}
|
||||
}
|
||||
case HAIR_COLOR_ID -> {
|
||||
for (Data hairColorData : data) {
|
||||
charHairColors.add(DataTool.getInt(hairColorData));
|
||||
}
|
||||
}
|
||||
case SKIN_ID -> {
|
||||
for (Data skinData : data) {
|
||||
charSkins.add(DataTool.getInt(skinData));
|
||||
}
|
||||
}
|
||||
case TOP_ID -> {
|
||||
for (Data topData : data) {
|
||||
charTops.add(DataTool.getInt(topData));
|
||||
}
|
||||
}
|
||||
case BOTTOM_ID -> {
|
||||
for (Data bottomData : data) {
|
||||
charBottoms.add(DataTool.getInt(bottomData));
|
||||
}
|
||||
}
|
||||
case SHOE_ID -> {
|
||||
for (Data shoeData : data) {
|
||||
charShoes.add(DataTool.getInt(shoeData));
|
||||
}
|
||||
}
|
||||
case WEAPON_ID -> {
|
||||
for (Data weaponData : data) {
|
||||
charWeapons.add(DataTool.getInt(weaponData));
|
||||
}
|
||||
}
|
||||
default -> log.error("Unhandled node inside MakeCharInfo.img.xml: '" + data.getName() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean verifyFaceId(int id) {
|
||||
return this.charFaces.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyHairId(int id) {
|
||||
if (id % 10 != 0) {
|
||||
return this.charHairs.contains(id - (id % 10));
|
||||
}
|
||||
return this.charHairs.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyHairColorId(int id) {
|
||||
return this.charHairColors.contains(id % 10);
|
||||
}
|
||||
|
||||
public boolean verifySkinId(int id) {
|
||||
return this.charSkins.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyTopId(int id) {
|
||||
return this.charTops.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyBottomId(int id) {
|
||||
return this.charBottoms.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyShoeId(int id) {
|
||||
return this.charShoes.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyWeaponId(int id) {
|
||||
return this.charWeapons.contains(id);
|
||||
}
|
||||
|
||||
public boolean verifyCharacter(Character character) {
|
||||
if (!verifyFaceId(character.getFace())) return false;
|
||||
if (!verifyHairId(character.getHair())) return false;
|
||||
if (!verifyHairColorId(character.getHair())) return false;
|
||||
if (!verifySkinId(character.getSkinColor().getId())) return false;
|
||||
|
||||
// Here we only verify the equipment if the character that's being created is of type 'Beginner'
|
||||
// This is because when the Maple Life A or Maple Life B items are used, the client does not send any data
|
||||
// regarding what equipment the character should be wearing (as it's all handled server-side)
|
||||
Job characterJob = character.getJob();
|
||||
if (characterJob == Job.BEGINNER || characterJob == Job.NOBLESSE || characterJob == Job.LEGEND) {
|
||||
if (!verifyTopId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -5).getItemId()))
|
||||
return false;
|
||||
if (!verifyBottomId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -6).getItemId()))
|
||||
return false;
|
||||
if (!verifyShoeId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -7).getItemId()))
|
||||
return false;
|
||||
if (!verifyWeaponId(character.getInventory(InventoryType.EQUIPPED).getItem((short) -11).getItemId()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
41
src/main/java/client/creator/MakeCharInfoValidator.java
Normal file
41
src/main/java/client/creator/MakeCharInfoValidator.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package client.creator;
|
||||
|
||||
import client.Character;
|
||||
import provider.Data;
|
||||
import provider.DataProviderFactory;
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
public class MakeCharInfoValidator {
|
||||
private static final MakeCharInfo charFemale;
|
||||
private static final MakeCharInfo charMale;
|
||||
private static final MakeCharInfo orientCharFemale;
|
||||
private static final MakeCharInfo orientCharMale;
|
||||
private static final MakeCharInfo premiumCharFemale;
|
||||
private static final MakeCharInfo premiumCharMale;
|
||||
|
||||
static {
|
||||
Data data = DataProviderFactory.getDataProvider(WZFiles.ETC).getData("MakeCharInfo.img");
|
||||
charFemale = new MakeCharInfo(data.getChildByPath("Info/CharFemale"));
|
||||
charMale = new MakeCharInfo(data.getChildByPath("Info/CharMale"));
|
||||
orientCharFemale = new MakeCharInfo(data.getChildByPath("OrientCharFemale"));
|
||||
orientCharMale = new MakeCharInfo(data.getChildByPath("OrientCharMale"));
|
||||
premiumCharFemale = new MakeCharInfo(data.getChildByPath("PremiumCharFemale"));
|
||||
premiumCharMale = new MakeCharInfo(data.getChildByPath("PremiumCharMale"));
|
||||
}
|
||||
|
||||
private static MakeCharInfo getMakeCharInfo(Character character) {
|
||||
return switch (character.getJob()) {
|
||||
case BEGINNER, WARRIOR, MAGICIAN, BOWMAN, THIEF, PIRATE -> character.isMale() ? charMale : charFemale;
|
||||
case NOBLESSE -> character.isMale() ? premiumCharMale : premiumCharFemale;
|
||||
case LEGEND -> character.isMale() ? orientCharMale : orientCharFemale;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean isNewCharacterValid(Character character) {
|
||||
MakeCharInfo makeCharInfo = getMakeCharInfo(character);
|
||||
if (makeCharInfo == null) return false;
|
||||
|
||||
return makeCharInfo.verifyCharacter(character);
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,6 @@ public class BeginnerCreator extends CharacterFactory {
|
||||
}
|
||||
|
||||
public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) {
|
||||
int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.BEGINNER, 1, MapId.MUSHROOM_TOWN, top, bottom, shoes, weapon));
|
||||
return status;
|
||||
return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.BEGINNER, 1, MapId.MUSHROOM_TOWN, top, bottom, shoes, weapon));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ public class LegendCreator extends CharacterFactory {
|
||||
}
|
||||
|
||||
public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) {
|
||||
int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.LEGEND, 1, MapId.ARAN_TUTORIAL_START, top, bottom, shoes, weapon));
|
||||
return status;
|
||||
return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.LEGEND, 1, MapId.ARAN_TUTORIAL_START, top, bottom, shoes, weapon));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ public class NoblesseCreator extends CharacterFactory {
|
||||
}
|
||||
|
||||
public static int createCharacter(Client c, String name, int face, int hair, int skin, int top, int bottom, int shoes, int weapon, int gender) {
|
||||
int status = createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.NOBLESSE, 1, MapId.STARTING_MAP_NOBLESSE, top, bottom, shoes, weapon));
|
||||
return status;
|
||||
return createNewCharacter(c, name, face, hair, skin, gender, createRecipe(Job.NOBLESSE, 1, MapId.STARTING_MAP_NOBLESSE, top, bottom, shoes, weapon));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,14 @@ import server.ItemInformationProvider;
|
||||
import server.ThreadManager;
|
||||
import tools.Pair;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -652,4 +659,4 @@ public class Inventory implements Iterable<Item> {
|
||||
public void dispose() {
|
||||
owner = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,11 @@ package client.inventory;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.Pair;
|
||||
|
||||
import java.sql.*;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
@@ -418,4 +422,4 @@ public enum ItemFactory {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,12 @@ package client.inventory.manipulator;
|
||||
import client.BuffStat;
|
||||
import client.Character;
|
||||
import client.Client;
|
||||
import client.inventory.*;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Inventory;
|
||||
import client.inventory.InventoryType;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ModifyInventory;
|
||||
import client.inventory.Pet;
|
||||
import client.newyear.NewYearCardRecord;
|
||||
import config.YamlConfig;
|
||||
import constants.id.ItemId;
|
||||
@@ -706,6 +711,12 @@ public class InventoryManipulator {
|
||||
Inventory inv = chr.getInventory(type);
|
||||
Item source = inv.getItem(src);
|
||||
|
||||
if (chr.isGM() && chr.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_DROP) {
|
||||
chr.message("You cannot drop items at your GM level.");
|
||||
log.info("GM %s tried to drop item id %d", chr.getName(), source.getItemId());
|
||||
return;
|
||||
}
|
||||
|
||||
if (chr.getTrade() != null || chr.getMiniGame() != null || source == null) { //Only check needed would prob be merchants (to see if the player is in one)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class KarmaManipulator {
|
||||
flag ^= karmaFlag;
|
||||
flag |= ItemConstants.UNTRADEABLE;
|
||||
|
||||
item.setFlag((byte) flag);
|
||||
item.setFlag(flag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,6 @@ public class KarmaManipulator {
|
||||
|
||||
flag |= karmaFlag;
|
||||
flag &= (0xFFFFFFFF ^ ItemConstants.UNTRADEABLE);
|
||||
item.setFlag((byte) flag);
|
||||
item.setFlag(flag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package client.processor.action; // thanks Alex for pointing out some package structures containing broad modules
|
||||
|
||||
import client.Character;
|
||||
import client.Client;
|
||||
import server.maps.MapleMap;
|
||||
import tools.PacketCreator;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class BuybackProcessor {
|
||||
|
||||
public static void processBuyback(Client c) {
|
||||
Character chr = c.getPlayer();
|
||||
boolean buyback;
|
||||
|
||||
c.lockClient();
|
||||
try {
|
||||
buyback = !chr.isAlive() && chr.couldBuyback();
|
||||
} finally {
|
||||
c.unlockClient();
|
||||
}
|
||||
|
||||
if (buyback) {
|
||||
String jobString;
|
||||
switch (chr.getJobStyle()) {
|
||||
case WARRIOR:
|
||||
jobString = "warrior";
|
||||
break;
|
||||
|
||||
case MAGICIAN:
|
||||
jobString = "magician";
|
||||
break;
|
||||
|
||||
case BOWMAN:
|
||||
jobString = "bowman";
|
||||
break;
|
||||
|
||||
case THIEF:
|
||||
jobString = "thief";
|
||||
break;
|
||||
|
||||
case BRAWLER:
|
||||
case GUNSLINGER:
|
||||
jobString = "pirate";
|
||||
break;
|
||||
|
||||
default:
|
||||
jobString = "beginner";
|
||||
}
|
||||
|
||||
chr.healHpMp();
|
||||
chr.purgeDebuffs();
|
||||
chr.broadcastStance(chr.isFacingLeft() ? 5 : 4);
|
||||
|
||||
MapleMap map = chr.getMap();
|
||||
map.broadcastMessage(PacketCreator.playSound("Buyback/" + jobString));
|
||||
map.broadcastMessage(PacketCreator.earnTitleMessage(chr.getName() + " just bought back into the game!"));
|
||||
|
||||
chr.sendPacket(PacketCreator.showBuybackEffect());
|
||||
map.broadcastMessage(chr, PacketCreator.showForeignBuybackEffect(chr.getId()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,12 @@ import tools.DatabaseConnection;
|
||||
import tools.PacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
import java.sql.*;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
@@ -92,14 +97,15 @@ public class DueyProcessor {
|
||||
}
|
||||
|
||||
private static Pair<Integer, Integer> getAccountCharacterIdFromCNAME(String name) {
|
||||
Pair<Integer, Integer> ids = null;
|
||||
Pair<Integer, Integer> ids = new Pair<>(-1, -1);
|
||||
try (Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT id,accountid FROM characters WHERE name = ?")) {
|
||||
ps.setString(1, name);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
ids = new Pair<>(rs.getInt("accountid"), rs.getInt("id"));
|
||||
ids.left = rs.getInt("accountid");
|
||||
ids.right = rs.getInt("id");
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
@@ -284,7 +290,20 @@ public class DueyProcessor {
|
||||
public static void dueySendItem(Client c, byte invTypeId, short itemPos, short amount, int sendMesos, String sendMessage, String recipient, boolean quick) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
if (c.getPlayer().isGM() && c.getPlayer().gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_USE_DUEY) {
|
||||
c.getPlayer().message("You cannot use Duey to send items at your GM level.");
|
||||
log.info(String.format("GM %s tried to send a package to %s", c.getPlayer().getName(), recipient));
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_INCORRECT_REQUEST.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
int fee = Trade.getFee(sendMesos);
|
||||
if (sendMessage != null && sendMessage.length() > 100) {
|
||||
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with Quick Delivery on duey.");
|
||||
log.warn("Chr {} tried to use duey with too long of a text", c.getPlayer().getName());
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
if (!quick) {
|
||||
fee += 5000;
|
||||
} else if (!c.getPlayer().haveItem(ItemId.QUICK_DELIVERY_TICKET)) {
|
||||
@@ -302,30 +321,25 @@ public class DueyProcessor {
|
||||
return;
|
||||
}
|
||||
|
||||
Pair<Integer, Integer> accIdCid;
|
||||
if (c.getPlayer().getMeso() >= finalcost) {
|
||||
accIdCid = getAccountCharacterIdFromCNAME(recipient);
|
||||
int recipientAccId = accIdCid.getLeft();
|
||||
if (recipientAccId != -1) {
|
||||
if (recipientAccId == c.getAccID()) {
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SAMEACC_ERROR.getCode()));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NAME_DOES_NOT_EXIST.getCode()));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(c.getPlayer().getMeso() < finalcost) {
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NOT_ENOUGH_MESOS.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
int recipientCid = accIdCid.getRight();
|
||||
if (recipientCid == -1) {
|
||||
var accIdCid = getAccountCharacterIdFromCNAME(recipient);
|
||||
var recipientAccId = accIdCid.getLeft();
|
||||
var recipientCid = accIdCid.getRight();
|
||||
|
||||
if (recipientAccId == -1 || recipientCid == -1) {
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NAME_DOES_NOT_EXIST.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (recipientAccId == c.getAccID()) {
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SAMEACC_ERROR.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (quick) {
|
||||
InventoryManipulator.removeById(c, InventoryType.CASH, ItemId.QUICK_DELIVERY_TICKET, (short) 1, false, false);
|
||||
}
|
||||
|
||||
@@ -36,12 +36,16 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import server.ItemInformationProvider;
|
||||
import server.maps.HiredMerchant;
|
||||
import service.NoteService;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.PacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.Collections;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -54,6 +58,12 @@ public class FredrickProcessor {
|
||||
private static final Logger log = LoggerFactory.getLogger(FredrickProcessor.class);
|
||||
private static final int[] dailyReminders = new int[]{2, 5, 10, 15, 30, 60, 90, Integer.MAX_VALUE};
|
||||
|
||||
private final NoteService noteService;
|
||||
|
||||
public FredrickProcessor(NoteService noteService) {
|
||||
this.noteService = noteService;
|
||||
}
|
||||
|
||||
private static byte canRetrieveFromFredrick(Character chr, List<Pair<Item, InventoryType>> items) {
|
||||
if (!Inventory.checkSpotsAndOwnership(chr, items)) {
|
||||
List<Integer> itemids = new LinkedList<>();
|
||||
@@ -127,10 +137,6 @@ public class FredrickProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeFredrickReminders(int cid) {
|
||||
removeFredrickReminders(Collections.singletonList(new Pair<>(cid, 0)));
|
||||
}
|
||||
|
||||
private static void removeFredrickReminders(List<Pair<Integer, Integer>> expiredCids) {
|
||||
List<String> expiredCnames = new LinkedList<>();
|
||||
for (Pair<Integer, Integer> id : expiredCids) {
|
||||
@@ -153,7 +159,7 @@ public class FredrickProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
public static void runFredrickSchedule() {
|
||||
public void runFredrickSchedule() {
|
||||
try (Connection con = DatabaseConnection.getConnection()) {
|
||||
List<Pair<Integer, Integer>> expiredCids = new LinkedList<>();
|
||||
List<Pair<Pair<Integer, String>, Integer>> notifCids = new LinkedList<>();
|
||||
@@ -241,7 +247,7 @@ public class FredrickProcessor {
|
||||
ps.addBatch();
|
||||
|
||||
String msg = fredrickReminderMessage(cid.getRight() - 1);
|
||||
Character.sendNote(cid.getLeft().getRight(), "FREDRICK", msg, (byte) 0);
|
||||
noteService.sendNormal(msg, "FREDRICK", cid.getLeft().getRight());
|
||||
}
|
||||
|
||||
ps.executeBatch();
|
||||
@@ -266,7 +272,7 @@ public class FredrickProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
public static void fredrickRetrieveItems(Client c) { // thanks Gustav for pointing out the dupe on Fredrick handling
|
||||
public void fredrickRetrieveItems(Client c) { // thanks Gustav for pointing out the dupe on Fredrick handling
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
Character chr = c.getPlayer();
|
||||
|
||||
@@ -50,6 +50,8 @@ public class StorageProcessor {
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
Character chr = c.getPlayer();
|
||||
Storage storage = chr.getStorage();
|
||||
String gmBlockedStorageMessage = "You cannot use the storage as a GM of this level.";
|
||||
|
||||
byte mode = p.readByte();
|
||||
|
||||
if (chr.getLevel() < 15) {
|
||||
@@ -61,7 +63,7 @@ public class StorageProcessor {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
switch (mode) {
|
||||
case 4: { // take out
|
||||
case 4: { // Take out
|
||||
byte type = p.readByte();
|
||||
byte slot = p.readByte();
|
||||
if (slot < 0 || slot > storage.getSlots()) { // removal starts at zero
|
||||
@@ -70,8 +72,17 @@ public class StorageProcessor {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
slot = storage.getSlot(InventoryType.getByType(type), slot);
|
||||
Item item = storage.getItem(slot);
|
||||
|
||||
if (hasGMRestrictions(chr)) {
|
||||
chr.dropMessage(1, gmBlockedStorageMessage);
|
||||
log.info(String.format("GM %s blocked from using storage", chr.getName()));
|
||||
chr.sendPacket(PacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (item != null) {
|
||||
if (ii.isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) {
|
||||
c.sendPacket(PacketCreator.getStorageError((byte) 0x0C));
|
||||
@@ -107,7 +118,7 @@ public class StorageProcessor {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5: { // store
|
||||
case 5: { // Store
|
||||
short slot = p.readShort();
|
||||
int itemId = p.readInt();
|
||||
short quantity = p.readShort();
|
||||
@@ -120,6 +131,14 @@ public class StorageProcessor {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasGMRestrictions(chr)) {
|
||||
chr.dropMessage(1, gmBlockedStorageMessage);
|
||||
log.info(String.format("GM %s blocked from using storage", chr.getName()));
|
||||
chr.sendPacket(PacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (quantity < 1) {
|
||||
c.sendPacket(PacketCreator.enableActions());
|
||||
return;
|
||||
@@ -173,16 +192,24 @@ public class StorageProcessor {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6: // arrange items
|
||||
case 6: // Arrange items
|
||||
if (YamlConfig.config.server.USE_STORAGE_ITEM_SORT) {
|
||||
storage.arrangeItems(c);
|
||||
}
|
||||
c.sendPacket(PacketCreator.enableActions());
|
||||
break;
|
||||
case 7: { // meso
|
||||
case 7: { // Mesos
|
||||
int meso = p.readInt();
|
||||
int storageMesos = storage.getMeso();
|
||||
int playerMesos = chr.getMeso();
|
||||
|
||||
if (hasGMRestrictions(chr)) {
|
||||
chr.dropMessage(1, gmBlockedStorageMessage);
|
||||
log.info(String.format("GM %s blocked from using storage", chr.getName()));
|
||||
chr.sendPacket(PacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if ((meso > 0 && storageMesos >= meso) || (meso < 0 && playerMesos >= -meso)) {
|
||||
if (meso < 0 && (storageMesos - meso) < 0) {
|
||||
meso = Integer.MIN_VALUE + storageMesos;
|
||||
@@ -208,7 +235,7 @@ public class StorageProcessor {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: // close... unless the player decides to enter cash shop!
|
||||
case 8: // Close (unless the player decides to enter cash shop)
|
||||
storage.close();
|
||||
break;
|
||||
}
|
||||
@@ -217,4 +244,8 @@ public class StorageProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasGMRestrictions(Character character) {
|
||||
return character.isGM() && character.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_USE_STORAGE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,22 @@
|
||||
package client.processor.stat;
|
||||
|
||||
import client.Character;
|
||||
import client.*;
|
||||
import client.Client;
|
||||
import client.Job;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.Stat;
|
||||
import client.autoban.AutobanFactory;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.InventoryType;
|
||||
import client.inventory.Item;
|
||||
import config.YamlConfig;
|
||||
import constants.skills.*;
|
||||
import constants.skills.BlazeWizard;
|
||||
import constants.skills.Brawler;
|
||||
import constants.skills.DawnWarrior;
|
||||
import constants.skills.Magician;
|
||||
import constants.skills.ThunderBreaker;
|
||||
import constants.skills.Warrior;
|
||||
import net.packet.InPacket;
|
||||
import tools.PacketCreator;
|
||||
import tools.Randomizer;
|
||||
@@ -543,16 +552,14 @@ public class AssignAPProcessor {
|
||||
return false;
|
||||
}
|
||||
|
||||
int hp = player.getMaxHp();
|
||||
int level_ = player.getLevel();
|
||||
if (hp < level_ * 14 + 148) {
|
||||
int hplose = -takeHp(player.getJob());
|
||||
if (player.getMaxHp() + hplose < getMinHp(player.getJob(), player.getLevel())) {
|
||||
player.message("You don't have the minimum HP pool required to swap.");
|
||||
c.sendPacket(PacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
int curHp = player.getHp();
|
||||
int hplose = -takeHp(player.getJob());
|
||||
player.assignHP(hplose, -1);
|
||||
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
|
||||
player.updateHp(Math.max(1, curHp + hplose));
|
||||
@@ -574,29 +581,14 @@ public class AssignAPProcessor {
|
||||
return false;
|
||||
}
|
||||
|
||||
int mp = player.getMaxMp();
|
||||
int level = player.getLevel();
|
||||
Job job = player.getJob();
|
||||
|
||||
boolean canWash = true;
|
||||
if (job.isA(Job.SPEARMAN) && mp < 4 * level + 156) {
|
||||
canWash = false;
|
||||
} else if ((job.isA(Job.FIGHTER) || job.isA(Job.ARAN1)) && mp < 4 * level + 56) {
|
||||
canWash = false;
|
||||
} else if (job.isA(Job.THIEF) && job.getId() % 100 > 0 && mp < level * 14 - 4) {
|
||||
canWash = false;
|
||||
} else if (mp < level * 14 + 148) {
|
||||
canWash = false;
|
||||
}
|
||||
|
||||
if (!canWash) {
|
||||
int mplose = -takeMp(player.getJob());
|
||||
if (player.getMaxMp() + mplose < getMinMp(player.getJob(), player.getLevel())) {
|
||||
player.message("You don't have the minimum MP pool required to swap.");
|
||||
c.sendPacket(PacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
int curMp = player.getMp();
|
||||
int mplose = -takeMp(job);
|
||||
player.assignMP(mplose, -1);
|
||||
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
|
||||
player.updateMp(Math.max(0, curMp + mplose));
|
||||
@@ -887,4 +879,109 @@ public class AssignAPProcessor {
|
||||
return MaxMP;
|
||||
}
|
||||
|
||||
public static int getMinHp(Job job, int level) {
|
||||
int multiplier = 0;
|
||||
int offset = 0;
|
||||
|
||||
if (job == Job.WARRIOR ||
|
||||
job.isA(Job.PAGE) ||
|
||||
job.isA(Job.SPEARMAN) ||
|
||||
job == Job.DAWNWARRIOR1 ||
|
||||
job == Job.ARAN1) {
|
||||
multiplier = 24; offset = 118;
|
||||
|
||||
} else if (job.isA(Job.FIGHTER) ||
|
||||
job.isA(Job.DAWNWARRIOR2) ||
|
||||
job.isA(Job.ARAN2)) {
|
||||
multiplier = 24; offset = 418;
|
||||
|
||||
} else if (job.isA(Job.MAGICIAN) ||
|
||||
job.isA(Job.BLAZEWIZARD1)) {
|
||||
multiplier = 10; offset = 54;
|
||||
|
||||
} else if (job == Job.BOWMAN ||
|
||||
job == Job.THIEF ||
|
||||
job == Job.WINDARCHER1 ||
|
||||
job == Job.NIGHTWALKER1) {
|
||||
multiplier = 20; offset = 58;
|
||||
|
||||
} else if (job.isA(Job.HUNTER) ||
|
||||
job.isA(Job.CROSSBOWMAN) ||
|
||||
job.isA(Job.ASSASSIN) ||
|
||||
job.isA(Job.BANDIT) ||
|
||||
job.isA(Job.WINDARCHER2) ||
|
||||
job.isA(Job.NIGHTWALKER2)) {
|
||||
multiplier = 20; offset = 358;
|
||||
|
||||
} else if (job == Job.PIRATE ||
|
||||
job == Job.THUNDERBREAKER1) {
|
||||
multiplier = 22; offset = 38;
|
||||
|
||||
} else if (job.isA(Job.BRAWLER) ||
|
||||
job.isA(Job.GUNSLINGER) ||
|
||||
job.isA(Job.THUNDERBREAKER2)) {
|
||||
multiplier = 22; offset = 338;
|
||||
|
||||
} else if (job == Job.BEGINNER ||
|
||||
job == Job.NOBLESSE) {
|
||||
multiplier = 12; offset = 38;
|
||||
}
|
||||
|
||||
return (multiplier * level) + offset;
|
||||
}
|
||||
|
||||
public static int getMinMp(Job job, int level) {
|
||||
int multiplier = 0;
|
||||
int offset = 0;
|
||||
|
||||
if (job == Job.WARRIOR ||
|
||||
job.isA(Job.FIGHTER) ||
|
||||
job.isA(Job.DAWNWARRIOR1) ||
|
||||
job.isA(Job.ARAN1)) {
|
||||
multiplier = 4; offset = 55;
|
||||
|
||||
} else if (job.isA(Job.PAGE) ||
|
||||
job.isA(Job.SPEARMAN)) {
|
||||
multiplier = 4; offset = 155;
|
||||
|
||||
} else if (job == Job.MAGICIAN ||
|
||||
job == Job.BLAZEWIZARD1) {
|
||||
multiplier = 22; offset = -1;
|
||||
|
||||
} else if (job.isA(Job.FP_WIZARD) ||
|
||||
job.isA(Job.IL_WIZARD) ||
|
||||
job.isA(Job.CLERIC) ||
|
||||
job.isA(Job.BLAZEWIZARD2)) {
|
||||
multiplier = 22; offset = 449;
|
||||
|
||||
} else if (job == Job.BOWMAN ||
|
||||
job == Job.THIEF ||
|
||||
job == Job.WINDARCHER1 ||
|
||||
job == Job.NIGHTWALKER1) {
|
||||
multiplier = 14; offset = -15;
|
||||
|
||||
} else if (job.isA(Job.HUNTER) ||
|
||||
job.isA(Job.CROSSBOWMAN) ||
|
||||
job.isA(Job.ASSASSIN) ||
|
||||
job.isA(Job.BANDIT) ||
|
||||
job.isA(Job.WINDARCHER2) ||
|
||||
job.isA(Job.NIGHTWALKER2)) {
|
||||
multiplier = 14; offset = 135;
|
||||
|
||||
} else if (job == Job.PIRATE ||
|
||||
job == Job.THUNDERBREAKER1) {
|
||||
multiplier = 18; offset = -55;
|
||||
|
||||
} else if (job.isA(Job.BRAWLER) ||
|
||||
job.isA(Job.GUNSLINGER) ||
|
||||
job.isA(Job.THUNDERBREAKER2)) {
|
||||
multiplier = 18; offset = 95;
|
||||
|
||||
} else if (job == Job.BEGINNER ||
|
||||
job == Job.NOBLESSE) {
|
||||
multiplier = 10; offset = -5;
|
||||
}
|
||||
|
||||
return (multiplier * level) + offset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ public class ServerConfig {
|
||||
public boolean USE_MTS;
|
||||
public boolean USE_CPQ;
|
||||
public boolean USE_AUTOHIDE_GM;
|
||||
public boolean USE_BUYBACK_SYSTEM;
|
||||
public boolean USE_FIXED_RATIO_HPMP_UPDATE;
|
||||
public boolean USE_FAMILY_SYSTEM;
|
||||
public boolean USE_DUEY;
|
||||
@@ -82,6 +81,7 @@ public class ServerConfig {
|
||||
public boolean USE_STARTING_AP_4;
|
||||
public boolean USE_AUTOBAN;
|
||||
public boolean USE_AUTOBAN_LOG;
|
||||
public boolean USE_EXP_GAIN_LOG;
|
||||
public boolean USE_AUTOSAVE;
|
||||
public boolean USE_SERVER_AUTOASSIGNER;
|
||||
public boolean USE_REFRESH_RANK_MOVE;
|
||||
@@ -97,7 +97,6 @@ public class ServerConfig {
|
||||
public boolean USE_ENFORCE_MERCHANT_SAVE;
|
||||
public boolean USE_ENFORCE_MDOOR_POSITION;
|
||||
public boolean USE_SPAWN_CLEAN_MDOOR;
|
||||
public boolean USE_SPAWN_LOOT_ON_ANIMATION;
|
||||
public boolean USE_SPAWN_RELEVANT_LOOT;
|
||||
public boolean USE_ERASE_PERMIT_ON_OPENSHOP;
|
||||
public boolean USE_ERASE_UNTRADEABLE_DROP;
|
||||
@@ -105,10 +104,8 @@ public class ServerConfig {
|
||||
public boolean USE_BUFF_MOST_SIGNIFICANT;
|
||||
public boolean USE_BUFF_EVERLASTING;
|
||||
public boolean USE_MULTIPLE_SAME_EQUIP_DROP;
|
||||
public boolean USE_BANISHABLE_TOWN_SCROLL;
|
||||
public boolean USE_ENABLE_FULL_RESPAWN;
|
||||
public boolean USE_ENABLE_CHAT_LOG;
|
||||
public boolean USE_REBIRTH_SYSTEM;
|
||||
public boolean USE_MAP_OWNERSHIP_SYSTEM;
|
||||
public boolean USE_FISHING_SYSTEM;
|
||||
public boolean USE_NPCS_SCRIPTABLE;
|
||||
@@ -134,10 +131,6 @@ public class ServerConfig {
|
||||
public boolean USE_MAKER_PERMISSIVE_ATKUP;
|
||||
public boolean USE_MAKER_FEE_HEURISTICS;
|
||||
|
||||
//Custom Configuration
|
||||
public boolean USE_ENABLE_CUSTOM_NPC_SCRIPT;
|
||||
public boolean USE_STARTER_MERGE;
|
||||
|
||||
//Commands Configuration
|
||||
public boolean BLOCK_GENERATE_CASH_ITEM;
|
||||
public boolean USE_WHOLE_SERVER_RANKING;
|
||||
@@ -166,7 +159,6 @@ public class ServerConfig {
|
||||
public long NAME_CHANGE_COOLDOWN;
|
||||
public long WORLD_TRANSFER_COOLDOWN = NAME_CHANGE_COOLDOWN;//Cooldown for world tranfers, default is same as name change (30 days).
|
||||
public boolean INSTANT_NAME_CHANGE;
|
||||
public int REBIRTH_NPC_ID;
|
||||
|
||||
//Dangling Items/Locks Configuration
|
||||
public int ITEM_EXPIRE_TIME;
|
||||
@@ -194,7 +186,6 @@ public class ServerConfig {
|
||||
public boolean USE_PERFECT_SCROLLING;
|
||||
public boolean USE_ENHANCED_CHSCROLL;
|
||||
public boolean USE_ENHANCED_CRAFTING;
|
||||
public boolean USE_ENHANCED_CLNSLATE;
|
||||
public int SCROLL_CHANCE_ROLLS;
|
||||
public int CHSCROLL_STAT_RATE;
|
||||
public int CHSCROLL_STAT_RANGE;
|
||||
@@ -244,7 +235,6 @@ public class ServerConfig {
|
||||
public boolean USE_EQUIPMNT_LVLUP_SLOTS;
|
||||
public boolean USE_EQUIPMNT_LVLUP_POWER;
|
||||
public boolean USE_EQUIPMNT_LVLUP_CASH;
|
||||
public boolean USE_SPIKES_AVOID_BANISH;
|
||||
public int MAX_EQUIPMNT_LVLUP_STAT_UP;
|
||||
public int MAX_EQUIPMNT_STAT;
|
||||
public int USE_EQUIPMNT_LVLUP;
|
||||
@@ -295,20 +285,18 @@ public class ServerConfig {
|
||||
public int WEDDING_GIFT_LIMIT;
|
||||
public boolean WEDDING_BLESSER_SHOWFX;
|
||||
|
||||
//Buyback Configuration
|
||||
public boolean USE_BUYBACK_WITH_MESOS;
|
||||
public float BUYBACK_FEE;
|
||||
public float BUYBACK_LEVEL_STACK_FEE;
|
||||
public int BUYBACK_MESO_MULTIPLIER;
|
||||
public int BUYBACK_RETURN_MINUTES;
|
||||
public int BUYBACK_COOLDOWN_MINUTES;
|
||||
|
||||
// Login timeout by shavit
|
||||
public long TIMEOUT_DURATION;
|
||||
|
||||
//Event End Timestamp
|
||||
public long EVENT_END_TIMESTAMP;
|
||||
|
||||
//GM Security Configuration
|
||||
public int MINIMUM_GM_LEVEL_TO_TRADE;
|
||||
public int MINIMUM_GM_LEVEL_TO_USE_STORAGE;
|
||||
public int MINIMUM_GM_LEVEL_TO_USE_DUEY;
|
||||
public int MINIMUM_GM_LEVEL_TO_DROP;
|
||||
|
||||
//Custom NPC overrides. List of NPC IDs.
|
||||
public Map<String, String> NPCS_SCRIPTABLE = new HashMap<>();
|
||||
}
|
||||
|
||||
8
src/main/java/constants/game/NpcChat.java
Normal file
8
src/main/java/constants/game/NpcChat.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package constants.game;
|
||||
|
||||
public final class NpcChat {
|
||||
public static final String NEW_LINE = "\r\n";
|
||||
|
||||
private NpcChat() {}
|
||||
|
||||
}
|
||||
@@ -95,46 +95,6 @@ public class ItemId {
|
||||
public static final int BEGINNERS_GUIDE = 4161001;
|
||||
public static final int LEGENDS_GUIDE = 4161048;
|
||||
public static final int NOBLESSE_GUIDE = 4161047;
|
||||
public static final int SWORD = 1302000; // Weapon
|
||||
public static final int HAND_AXE = 1312004;
|
||||
public static final int WOODEN_CLUB = 1322005;
|
||||
public static final int BASIC_POLEARM = 1442079;
|
||||
public static final int WHITE_UNDERSHIRT = 1040002; // Top
|
||||
public static final int UNDERSHIRT = 1040006;
|
||||
public static final int GREY_TSHIRT = 1040010;
|
||||
public static final int WHITE_TUBETOP = 1041002;
|
||||
public static final int YELLOW_TSHIRT = 1041006;
|
||||
public static final int GREEN_TSHIRT = 1041010;
|
||||
public static final int RED_STRIPED_TOP = 1041011;
|
||||
public static final int SIMPLE_WARRIOR_TOP = 1042167;
|
||||
public static final int BLUE_JEAN_SHORTS = 1060002; // Bottom
|
||||
public static final int BROWN_COTTON_SHORTS = 1060006;
|
||||
public static final int RED_MINISKIRT = 1061002;
|
||||
public static final int INDIGO_MINISKIRT = 1061008;
|
||||
public static final int SIMPLE_WARRIOR_PANTS = 1062115;
|
||||
public static final int RED_RUBBER_BOOTS = 1072001;
|
||||
public static final int LEATHER_SANDALS = 1072005;
|
||||
public static final int YELLOW_RUBBER_BOOTS = 1072037;
|
||||
public static final int BLUE_RUBBER_BOOTS = 1072038;
|
||||
public static final int AVERAGE_MUSASHI_SHOES = 1072383;
|
||||
public static final int BLACK_TOBEN = 30000; // Hair
|
||||
public static final int ZETA = 30010;
|
||||
public static final int BLACK_REBEL = 30020;
|
||||
public static final int BLACK_BUZZ = 30030;
|
||||
public static final int BLACK_SAMMY = 31000;
|
||||
public static final int BLACK_EDGY = 31040;
|
||||
public static final int BLACK_CONNIE = 31050;
|
||||
public static final int MOTIVATED_LOOK_M = 20000; // Face
|
||||
public static final int PERPLEXED_STARE = 20001;
|
||||
public static final int LEISURE_LOOK_M = 20002;
|
||||
public static final int MOTIVATED_LOOK_F = 21000;
|
||||
public static final int FEARFUL_STARE_M = 21001;
|
||||
public static final int LEISURE_LOOK_F = 21002;
|
||||
public static final int FEARFUL_STARE_F = 21201;
|
||||
public static final int PERPLEXED_STARE_HAZEL = 20401;
|
||||
public static final int LEISURE_LOOK_HAZEL = 20402;
|
||||
public static final int MOTIVATED_LOOK_AMETHYST = 21700;
|
||||
public static final int MOTIVATED_LOOK_BLUE = 20100;
|
||||
|
||||
// Warrior
|
||||
public static final int RED_HWARANG_SHIRT = 1040021;
|
||||
@@ -205,7 +165,6 @@ public class ItemId {
|
||||
public static final int EYEDROP = 2050001;
|
||||
public static final int TONIC = 2050002;
|
||||
public static final int HOLY_WATER = 2050003;
|
||||
public static final int ANTI_BANISH_SCROLL = 2030100;
|
||||
private static final int DOJO_PARTY_ALL_CURE = 2022433;
|
||||
private static final int CARNIVAL_PARTY_ALL_CURE = 2022163;
|
||||
public static final int WHITE_ELIXIR = 2022544;
|
||||
@@ -352,6 +311,10 @@ public class ItemId {
|
||||
return itemId == NX_CARD_100 || itemId == NX_CARD_250;
|
||||
}
|
||||
|
||||
public static boolean isCashPackage(int itemId) {
|
||||
return itemId / 10000 == 910;
|
||||
}
|
||||
|
||||
// Face expression
|
||||
private static final int FACE_EXPRESSION_MIN = 5160000;
|
||||
private static final int FACE_EXPRESSION_MAX = 5160014;
|
||||
|
||||
@@ -134,11 +134,7 @@ public final class ItemConstants {
|
||||
}
|
||||
|
||||
public static boolean isTownScroll(int itemId) {
|
||||
return itemId >= 2030000 && itemId < ItemId.ANTI_BANISH_SCROLL;
|
||||
}
|
||||
|
||||
public static boolean isAntibanishScroll(int itemId) {
|
||||
return itemId == ItemId.ANTI_BANISH_SCROLL;
|
||||
return itemId >= 2030000;
|
||||
}
|
||||
|
||||
public static boolean isCleanSlate(int scrollId) {
|
||||
|
||||
10
src/main/java/database/DaoException.java
Normal file
10
src/main/java/database/DaoException.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package database;
|
||||
|
||||
import org.jdbi.v3.core.JdbiException;
|
||||
|
||||
public class DaoException extends JdbiException {
|
||||
|
||||
public DaoException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
44
src/main/java/database/DatabaseMigrations.java
Normal file
44
src/main/java/database/DatabaseMigrations.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package database;
|
||||
|
||||
import liquibase.Liquibase;
|
||||
import liquibase.UpdateSummaryOutputEnum;
|
||||
import liquibase.database.jvm.JdbcConnection;
|
||||
import liquibase.exception.LiquibaseException;
|
||||
import liquibase.resource.ClassLoaderResourceAccessor;
|
||||
import tools.DatabaseConnection;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Apply changes to the database so that the server and database work in harmony.
|
||||
*
|
||||
* @author Ponk
|
||||
*/
|
||||
public class DatabaseMigrations {
|
||||
private static final String ROOT_CHANGELOG_FILE = "db/changelog-root.xml";
|
||||
|
||||
public static void runDatabaseMigrations() {
|
||||
suppressLiquibaseLogs();
|
||||
runLiquibaseUpdate();
|
||||
}
|
||||
|
||||
private static void suppressLiquibaseLogs() {
|
||||
Logger liquibaseLogger = Logger.getLogger("liquibase");
|
||||
liquibaseLogger.setLevel(Level.WARNING);
|
||||
}
|
||||
|
||||
private static void runLiquibaseUpdate() {
|
||||
try (Connection connection = DatabaseConnection.getConnection()) {
|
||||
liquibase.database.DatabaseConnection databaseConnection = new JdbcConnection(connection);
|
||||
Liquibase liquibase = new Liquibase(ROOT_CHANGELOG_FILE, new ClassLoaderResourceAccessor(),
|
||||
databaseConnection);
|
||||
liquibase.setShowSummaryOutput(UpdateSummaryOutputEnum.LOG);
|
||||
liquibase.update();
|
||||
} catch (SQLException | LiquibaseException e) {
|
||||
throw new RuntimeException("Failed to run database migrations", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
89
src/main/java/database/note/NoteDao.java
Normal file
89
src/main/java/database/note/NoteDao.java
Normal file
@@ -0,0 +1,89 @@
|
||||
package database.note;
|
||||
|
||||
import database.DaoException;
|
||||
import model.Note;
|
||||
import org.jdbi.v3.core.Handle;
|
||||
import org.jdbi.v3.core.JdbiException;
|
||||
import tools.DatabaseConnection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class NoteDao {
|
||||
|
||||
public void save(Note note) {
|
||||
try (Handle handle = DatabaseConnection.getHandle()) {
|
||||
handle.createUpdate("""
|
||||
INSERT INTO notes (`message`, `from`, `to`, `timestamp`, `fame`, `deleted`)
|
||||
VALUES (?, ?, ?, ?, ?, ?)""")
|
||||
.bind(0, note.message())
|
||||
.bind(1, note.from())
|
||||
.bind(2, note.to())
|
||||
.bind(3, note.timestamp())
|
||||
.bind(4, note.fame())
|
||||
.bind(5, 0)
|
||||
.execute();
|
||||
} catch (JdbiException e) {
|
||||
throw new DaoException("Failed to save note: %s".formatted(note.toString()), e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Note> findAllByTo(String to) {
|
||||
try (Handle handle = DatabaseConnection.getHandle()) {
|
||||
return handle.createQuery("""
|
||||
SELECT *
|
||||
FROM notes
|
||||
WHERE `deleted` = 0
|
||||
AND `to` = ?""")
|
||||
.bind(0, to)
|
||||
.mapTo(Note.class)
|
||||
.list();
|
||||
} catch (JdbiException e) {
|
||||
throw new DaoException("Failed to find notes sent to: %s".formatted(to), e);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<Note> delete(int id) {
|
||||
try (Handle handle = DatabaseConnection.getHandle()) {
|
||||
Optional<Note> note = findById(handle, id);
|
||||
if (note.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
deleteById(handle, id);
|
||||
|
||||
return note;
|
||||
} catch (JdbiException e) {
|
||||
throw new DaoException("Failed to delete note with id: %d".formatted(id), e);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<Note> findById(Handle handle, int id) {
|
||||
final Optional<Note> note;
|
||||
try {
|
||||
note = handle.createQuery("""
|
||||
SELECT *
|
||||
FROM notes
|
||||
WHERE `deleted` = 0
|
||||
AND `id` = ?""")
|
||||
.bind(0, id)
|
||||
.mapTo(Note.class)
|
||||
.findOne();
|
||||
} catch (JdbiException e) {
|
||||
throw new DaoException("Failed find note with id %s".formatted(id), e);
|
||||
}
|
||||
return note;
|
||||
}
|
||||
|
||||
private void deleteById(Handle handle, int id) {
|
||||
try {
|
||||
handle.createUpdate("""
|
||||
UPDATE notes
|
||||
SET `deleted` = 1
|
||||
WHERE `id` = ?""")
|
||||
.bind(0, id)
|
||||
.execute();
|
||||
} catch (JdbiException e) {
|
||||
throw new DaoException("Failed to delete note with id %d".formatted(id), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/main/java/database/note/NoteRowMapper.java
Normal file
22
src/main/java/database/note/NoteRowMapper.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package database.note;
|
||||
|
||||
import model.Note;
|
||||
import org.jdbi.v3.core.mapper.RowMapper;
|
||||
import org.jdbi.v3.core.statement.StatementContext;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class NoteRowMapper implements RowMapper<Note> {
|
||||
|
||||
@Override
|
||||
public Note map(ResultSet rs, StatementContext ctx) throws SQLException {
|
||||
int id = rs.getInt("id");
|
||||
String message = rs.getString("message");
|
||||
String from = rs.getString("from");
|
||||
String to = rs.getString("to");
|
||||
long timestamp = rs.getLong("timestamp");
|
||||
int fame = rs.getInt("fame");
|
||||
return new Note(id, message, from, to, timestamp, fame);
|
||||
}
|
||||
}
|
||||
21
src/main/java/model/Note.java
Normal file
21
src/main/java/model/Note.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record Note(int id, String message, String from, String to, long timestamp, int fame) {
|
||||
private static final int PLACEHOLDER_ID = -1;
|
||||
|
||||
public Note {
|
||||
Objects.requireNonNull(message);
|
||||
Objects.requireNonNull(from);
|
||||
Objects.requireNonNull(to);
|
||||
}
|
||||
|
||||
public static Note createNormal(String message, String from, String to, long timestamp) {
|
||||
return new Note(PLACEHOLDER_ID, message, from, to, timestamp, 0);
|
||||
}
|
||||
|
||||
public static Note createGift(String message, String from, String to, long timestamp) {
|
||||
return new Note(PLACEHOLDER_ID, message, from, to, timestamp, 1);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user